From 03f5375a02acb3bcca7039e872165768529c47db Mon Sep 17 00:00:00 2001 From: Andrew Shvayka Date: Fri, 14 Feb 2020 19:18:18 +0200 Subject: [PATCH 1/9] JSON support (#2415) * Created JsonDataEntry and added DataType JSON * Added json to ts and attributes, created sql schema-entities-hsql.sql (json_v varchar) * refactored * refactored * added json array support * Aggregation improvement * Changed in JsonDataEntry value type from JsonNode to String * fix AggregatePartitionsFunction Co-authored-by: Yevhen Bondarenko <56396344+YevhenBondarenko@users.noreply.github.com> --- .../device/DeviceActorMessageProcessor.java | 7 + .../server/controller/BaseController.java | 2 + .../controller/TelemetryController.java | 43 ++- .../DefaultTelemetrySubscriptionService.java | 26 +- application/src/main/proto/cluster.proto | 1 + .../controller/ControllerSqlTestSuite.java | 2 +- .../server/mqtt/MqttSqlTestSuite.java | 2 +- .../server/rules/RuleEngineSqlTestSuite.java | 2 +- .../server/system/SystemSqlTestSuite.java | 2 +- .../SearchTextBasedWithAdditionalInfo.java | 3 +- .../common/data/kv/BaseAttributeKvEntry.java | 7 + .../server/common/data/kv/BasicKvEntry.java | 5 + .../server/common/data/kv/BasicTsKvEntry.java | 5 + .../server/common/data/kv/DataType.java | 2 +- .../server/common/data/kv/JsonDataEntry.java | 69 +++++ .../server/common/data/kv/KvEntry.java | 2 + .../transport/adaptor/JsonConverter.java | 40 ++- .../src/main/proto/transport.proto | 2 + .../CassandraBaseAttributesDao.java | 40 +-- .../server/dao/model/ModelConstants.java | 17 +- .../dao/model/sql/AbstractTsKvEntity.java | 8 +- .../dao/model/sql/AttributeKvEntity.java | 8 + .../dao/model/sqlts/hsql/TsKvEntity.java | 18 +- .../model/sqlts/latest/TsKvLatestEntity.java | 6 +- .../dao/model/sqlts/psql/TsKvEntity.java | 15 +- .../sqlts/timescale/TimescaleTsKvEntity.java | 17 +- .../AttributeKvInsertRepository.java | 118 ++------ .../HsqlAttributesInsertRepository.java | 34 +-- .../dao/sql/attributes/JpaAttributeDao.java | 1 + .../PsqlAttributesInsertRepository.java | 19 -- .../dao/sqlts/AbstractSqlTimeseriesDao.java | 2 + .../sqlts/hsql/HsqlInsertTsRepository.java | 12 +- .../dao/sqlts/hsql/TsKvHsqlRepository.java | 3 +- .../latest/HsqlLatestInsertTsRepository.java | 8 +- .../latest/PsqlLatestInsertTsRepository.java | 33 ++- .../latest/SearchTsKvLatestRepository.java | 3 +- .../sqlts/latest/TsKvLatestRepository.java | 4 - .../dao/sqlts/psql/JpaPsqlTimeseriesDao.java | 1 + .../sqlts/psql/PsqlInsertTsRepository.java | 21 +- .../dao/sqlts/psql/TsKvPsqlRepository.java | 3 +- .../timescale/AggregationRepository.java | 9 +- .../TimescaleInsertTsRepository.java | 19 +- .../timescale/TimescaleTimeseriesDao.java | 2 + .../AggregatePartitionsFunction.java | 48 +++- .../CassandraBaseTimeseriesDao.java | 59 ++-- .../resources/cassandra/schema-entities.cql | 1 + .../main/resources/cassandra/schema-ts.cql | 2 + .../resources/sql/schema-entities-hsql.sql | 251 ++++++++++++++++++ .../main/resources/sql/schema-entities.sql | 1 + .../main/resources/sql/schema-timescale.sql | 2 + dao/src/main/resources/sql/schema-ts-hsql.sql | 2 + dao/src/main/resources/sql/schema-ts-psql.sql | 4 +- .../server/dao/JpaDaoTestSuite.java | 2 +- .../server/dao/SqlDaoServiceTestSuite.java | 2 +- .../metadata/TbAbstractGetAttributesNode.java | 11 +- .../engine/metadata/TbGetTelemetryNode.java | 9 + 56 files changed, 713 insertions(+), 324 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/kv/JsonDataEntry.java create mode 100644 dao/src/main/resources/sql/schema-entities-hsql.sql diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index 7eb91847cf..1be7e18aab 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -567,6 +567,9 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { case STRING_V: json.addProperty(kv.getKey(), kv.getStringV()); break; + case JSON_V: + json.add(kv.getKey(), jsonParser.parse(kv.getJsonV())); + break; } } return json; @@ -643,6 +646,10 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { builder.setType(KeyValueType.STRING_V); builder.setStringV(kvEntry.getStrValue().get()); break; + case JSON: + builder.setType(KeyValueType.JSON_V); + builder.setJsonV(kvEntry.getJsonValue().get()); + break; } return builder.build(); } 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 124a6e7ba1..634a78de27 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -642,6 +642,8 @@ public abstract class BaseController { entityNode.put(attr.getKey(), attr.getDoubleValue().get()); } else if (attr.getDataType() == DataType.LONG) { entityNode.put(attr.getKey(), attr.getLongValue().get()); + } else if (attr.getDataType() == DataType.JSON) { + entityNode.set(attr.getKey(), json.readTree(attr.getJsonValue().get())); } else { entityNode.put(attr.getKey(), attr.getValueAsString()); } 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 43b525021e..86a2457e99 100644 --- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java +++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java @@ -15,12 +15,15 @@ */ package org.thingsboard.server.controller; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Function; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; import com.google.gson.JsonParser; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -56,8 +59,10 @@ 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; import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; import org.thingsboard.server.common.data.kv.DoubleDataEntry; +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; @@ -77,6 +82,7 @@ import org.thingsboard.server.service.telemetry.exception.UncheckedApiException; import javax.annotation.Nullable; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; @@ -107,6 +113,8 @@ public class TelemetryController extends BaseController { private ExecutorService executor; + private static final ObjectMapper mapper = new ObjectMapper(); + @PostConstruct public void initExecutor() { executor = Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName("telemetry-controller")); @@ -284,8 +292,7 @@ public class TelemetryController extends BaseController { if (startTs == null || endTs == null) { deleteToTs = endTs; return getImmediateDeferredResult("When deleteAllDataForKeys is false, start and end timestamp values shouldn't be empty", HttpStatus.BAD_REQUEST); - } - else{ + } else { deleteFromTs = startTs; deleteToTs = endTs; } @@ -536,8 +543,9 @@ public class TelemetryController extends BaseController { return new FutureCallback>() { @Override public void onSuccess(List attributes) { - List values = attributes.stream().map(attribute -> new AttributeData(attribute.getLastUpdateTs(), - attribute.getKey(), attribute.getValue())).collect(Collectors.toList()); + List values = attributes.stream().map(attribute -> + new AttributeData(attribute.getLastUpdateTs(), attribute.getKey(), getKvValue(attribute)) + ).collect(Collectors.toList()); logAttributesRead(user, entityId, scope, keyList, null); response.setResult(new ResponseEntity<>(values, HttpStatus.OK)); } @@ -639,7 +647,9 @@ public class TelemetryController extends BaseController { jsonNode.fields().forEachRemaining(entry -> { String key = entry.getKey(); JsonNode value = entry.getValue(); - if (entry.getValue().isTextual()) { + if (entry.getValue().isObject() || entry.getValue().isArray()) { + attributes.add(new BaseAttributeKvEntry(new JsonDataEntry(key, toJsonStr(value)), ts)); + } else if (entry.getValue().isTextual()) { if (maxStringValueLength > 0 && entry.getValue().textValue().length() > maxStringValueLength) { String message = String.format("String value length [%d] for key [%s] is greater than maximum allowed [%d]", entry.getValue().textValue().length(), key, maxStringValueLength); throw new UncheckedApiException(new InvalidParametersException(message)); @@ -659,4 +669,27 @@ public class TelemetryController extends BaseController { }); return attributes; } + + private String toJsonStr(JsonNode value) { + try { + return mapper.writeValueAsString(value); + } catch (JsonProcessingException e) { + throw new JsonParseException("Can't parse jsonValue: " + value, e); + } + } + + private JsonNode toJsonNode(String value) { + try { + return mapper.readTree(value); + } catch (IOException e) { + throw new JsonParseException("Can't parse jsonValue: " + value, e); + } + } + + private Object getKvValue(KvEntry entry) { + if (entry.getDataType() == DataType.JSON) { + return toJsonNode(entry.getJsonValue().get()); + } + return entry.getValue(); + } } 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 45134a6bdd..d29e5de5b7 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 @@ -24,9 +24,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; +import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; -import org.thingsboard.common.util.DonAsynchron; import org.thingsboard.server.actors.service.ActorService; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.EntityType; @@ -36,7 +36,20 @@ 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.TenantId; -import org.thingsboard.server.common.data.kv.*; +import org.thingsboard.server.common.data.kv.Aggregation; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +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; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +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.msg.cluster.SendToClusterMsg; import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.dao.attributes.AttributesService; @@ -105,7 +118,7 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio @Autowired @Lazy private ActorService actorService; - + private ExecutorService tsCallBackExecutor; private ExecutorService wsCallBackExecutor; @@ -692,6 +705,10 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio Optional doubleValue = attr.getDoubleValue(); doubleValue.ifPresent(dataBuilder::setDoubleValue); break; + case JSON: + Optional jsonValue = attr.getJsonValue(); + jsonValue.ifPresent(dataBuilder::setJsonValue); + break; case STRING: Optional stringValue = attr.getStrValue(); stringValue.ifPresent(dataBuilder::setStrValue); @@ -724,6 +741,9 @@ public class DefaultTelemetrySubscriptionService implements TelemetrySubscriptio case STRING: entry = new StringDataEntry(proto.getKey(), proto.getStrValue()); break; + case JSON: + entry = new JsonDataEntry(proto.getKey(), proto.getJsonValue()); + break; } return entry; } diff --git a/application/src/main/proto/cluster.proto b/application/src/main/proto/cluster.proto index b4ebc52f5e..cfacc66121 100644 --- a/application/src/main/proto/cluster.proto +++ b/application/src/main/proto/cluster.proto @@ -125,6 +125,7 @@ message KeyValueProto { int64 longValue = 5; double doubleValue = 6; bool boolValue = 7; + string jsonValue = 8; } message FromDeviceRPCResponseProto { diff --git a/application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java index 4fe33e4716..8dc0acff57 100644 --- a/application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/controller/ControllerSqlTestSuite.java @@ -30,7 +30,7 @@ public class ControllerSqlTestSuite { @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql"), + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql"), "sql/drop-all-tables.sql", "sql-test.properties"); } diff --git a/application/src/test/java/org/thingsboard/server/mqtt/MqttSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/mqtt/MqttSqlTestSuite.java index 5fb8c4d0c7..2863589ba1 100644 --- a/application/src/test/java/org/thingsboard/server/mqtt/MqttSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/mqtt/MqttSqlTestSuite.java @@ -29,7 +29,7 @@ public class MqttSqlTestSuite { @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/system-data.sql"), + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), "sql/drop-all-tables.sql", "sql-test.properties"); } diff --git a/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java index ce2c6852be..5f930821f7 100644 --- a/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/rules/RuleEngineSqlTestSuite.java @@ -30,7 +30,7 @@ public class RuleEngineSqlTestSuite { @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/system-data.sql"), + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), "sql/drop-all-tables.sql", "sql-test.properties"); } diff --git a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java index 3cbb7d9773..b12d513ce0 100644 --- a/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java +++ b/application/src/test/java/org/thingsboard/server/system/SystemSqlTestSuite.java @@ -31,7 +31,7 @@ public class SystemSqlTestSuite { @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/system-data.sql"), + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), "sql/drop-all-tables.sql", "sql-test.properties"); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java index ecbd2c73f5..8dc9bf6abc 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java @@ -35,6 +35,7 @@ import java.util.function.Consumer; @Slf4j public abstract class SearchTextBasedWithAdditionalInfo extends SearchTextBased implements HasAdditionalInfo { + private static final ObjectMapper mapper = new ObjectMapper(); private transient JsonNode additionalInfo; @JsonIgnore private byte[] additionalInfoBytes; @@ -97,7 +98,7 @@ public abstract class SearchTextBasedWithAdditionalInfo ext public static void setJson(JsonNode json, Consumer jsonConsumer, Consumer bytesConsumer) { jsonConsumer.accept(json); try { - bytesConsumer.accept(new ObjectMapper().writeValueAsBytes(json)); + bytesConsumer.accept(mapper.writeValueAsBytes(json)); } catch (JsonProcessingException e) { log.warn("Can't serialize json data: ", e); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseAttributeKvEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseAttributeKvEntry.java index ac8a5c2f78..5639f98d01 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseAttributeKvEntry.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseAttributeKvEntry.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.common.data.kv; +import com.fasterxml.jackson.databind.JsonNode; + import java.util.Optional; /** @@ -65,6 +67,11 @@ public class BaseAttributeKvEntry implements AttributeKvEntry { return kv.getDoubleValue(); } + @Override + public Optional getJsonValue() { + return kv.getJsonValue(); + } + @Override public String getValueAsString() { return kv.getValueAsString(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BasicKvEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BasicKvEntry.java index a41bf6b232..7bc92ff74c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BasicKvEntry.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BasicKvEntry.java @@ -51,6 +51,11 @@ public abstract class BasicKvEntry implements KvEntry { return Optional.ofNullable(null); } + @Override + public Optional getJsonValue() { + return Optional.ofNullable(null); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BasicTsKvEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BasicTsKvEntry.java index f7628da74c..c2d6688004 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BasicTsKvEntry.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BasicTsKvEntry.java @@ -58,6 +58,11 @@ public class BasicTsKvEntry implements TsKvEntry { return kv.getDoubleValue(); } + @Override + public Optional getJsonValue() { + return kv.getJsonValue(); + } + @Override public Object getValue() { return kv.getValue(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java index 84f918ede3..3571b0c882 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/DataType.java @@ -17,6 +17,6 @@ package org.thingsboard.server.common.data.kv; public enum DataType { - STRING, LONG, BOOLEAN, DOUBLE; + STRING, LONG, BOOLEAN, DOUBLE, JSON; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/JsonDataEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/JsonDataEntry.java new file mode 100644 index 0000000000..0510f311d5 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/JsonDataEntry.java @@ -0,0 +1,69 @@ +/** + * Copyright © 2016-2020 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.Objects; +import java.util.Optional; + +public class JsonDataEntry extends BasicKvEntry { + private final String value; + + public JsonDataEntry(String key, String value) { + super(key); + this.value = value; + } + + @Override + public DataType getDataType() { + return DataType.JSON; + } + + @Override + public Optional getJsonValue() { + return Optional.ofNullable(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof JsonDataEntry)) return false; + if (!super.equals(o)) return false; + JsonDataEntry that = (JsonDataEntry) o; + return Objects.equals(value, that.value); + } + + @Override + public Object getValue() { + return value; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), value); + } + + @Override + public String toString() { + return "JsonDataEntry{" + + "value=" + value + + "} " + super.toString(); + } + + @Override + public String getValueAsString() { + return value; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/KvEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/KvEntry.java index c8753fda8b..296ddd37aa 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/KvEntry.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/KvEntry.java @@ -37,6 +37,8 @@ public interface KvEntry extends Serializable { Optional getDoubleValue(); + Optional getJsonValue(); + String getValueAsString(); Object getValue(); diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java index 03f840a17e..56e2c87d6e 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/adaptor/JsonConverter.java @@ -31,6 +31,7 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry; +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.StringDataEntry; @@ -59,6 +60,7 @@ import java.util.stream.Collectors; public class JsonConverter { private static final Gson GSON = new Gson(); + private static final JsonParser JSON_PARSER = new JsonParser(); private static final String CAN_T_PARSE_VALUE = "Can't parse value: "; private static final String DEVICE_PROPERTY = "device"; @@ -204,6 +206,14 @@ public class JsonConverter { } else if (!value.isJsonNull()) { throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value); } + } else if (element.isJsonObject() || element.isJsonArray()) { + result.add(KeyValueProto + .newBuilder() + .setKey(valueEntry + .getKey()) + .setType(KeyValueType.JSON_V) + .setJsonV(element.toString()) + .build()); } else if (!element.isJsonNull()) { throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element); } @@ -354,6 +364,9 @@ public class JsonConverter { case LONG_V: json.addProperty(name, entry.getLongV()); break; + case JSON_V: + json.add(name, JSON_PARSER.parse(entry.getJsonV())); + break; } } @@ -363,47 +376,48 @@ public class JsonConverter { private static Consumer addToObjectFromProto(JsonObject result) { return de -> { - JsonPrimitive value; switch (de.getKv().getType()) { case BOOLEAN_V: - value = new JsonPrimitive(de.getKv().getBoolV()); + result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getBoolV())); break; case DOUBLE_V: - value = new JsonPrimitive(de.getKv().getDoubleV()); + result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getDoubleV())); break; case LONG_V: - value = new JsonPrimitive(de.getKv().getLongV()); + result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getLongV())); break; case STRING_V: - value = new JsonPrimitive(de.getKv().getStringV()); + result.add(de.getKv().getKey(), new JsonPrimitive(de.getKv().getStringV())); break; + case JSON_V: + result.add(de.getKv().getKey(), JSON_PARSER.parse(de.getKv().getJsonV())); default: throw new IllegalArgumentException("Unsupported data type: " + de.getKv().getType()); } - result.add(de.getKv().getKey(), value); }; } private static Consumer addToObject(JsonObject result) { return de -> { - JsonPrimitive value; switch (de.getDataType()) { case BOOLEAN: - value = new JsonPrimitive(de.getBooleanValue().get()); + result.add(de.getKey(), new JsonPrimitive(de.getBooleanValue().get())); break; case DOUBLE: - value = new JsonPrimitive(de.getDoubleValue().get()); + result.add(de.getKey(), new JsonPrimitive(de.getDoubleValue().get())); break; case LONG: - value = new JsonPrimitive(de.getLongValue().get()); + result.add(de.getKey(), new JsonPrimitive(de.getLongValue().get())); break; case STRING: - value = new JsonPrimitive(de.getStrValue().get()); + result.add(de.getKey(), new JsonPrimitive(de.getStrValue().get())); + break; + case JSON: + result.add(de.getKey(), JSON_PARSER.parse(de.getJsonValue().get())); break; default: throw new IllegalArgumentException("Unsupported data type: " + de.getDataType()); } - result.add(de.getKey(), value); }; } @@ -464,6 +478,8 @@ public class JsonConverter { } else { throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value); } + } else if (element.isJsonObject() || element.isJsonArray()) { + result.add(new JsonDataEntry(valueEntry.getKey(), element.toString())); } else { throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element); } diff --git a/common/transport/transport-api/src/main/proto/transport.proto b/common/transport/transport-api/src/main/proto/transport.proto index 2d536b1769..e8b513574a 100644 --- a/common/transport/transport-api/src/main/proto/transport.proto +++ b/common/transport/transport-api/src/main/proto/transport.proto @@ -47,6 +47,7 @@ enum KeyValueType { LONG_V = 1; DOUBLE_V = 2; STRING_V = 3; + JSON_V = 4; } message KeyValueProto { @@ -56,6 +57,7 @@ message KeyValueProto { int64 long_v = 4; double double_v = 5; string string_v = 6; + string json_v = 7; } message TsKvProto { diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/CassandraBaseAttributesDao.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/CassandraBaseAttributesDao.java index 93a39f8ef4..481f2ac085 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/CassandraBaseAttributesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/CassandraBaseAttributesDao.java @@ -112,31 +112,18 @@ public class CassandraBaseAttributesDao extends CassandraAbstractAsyncDao implem @Override public ListenableFuture save(TenantId tenantId, EntityId entityId, String attributeType, AttributeKvEntry attribute) { - BoundStatement stmt = getSaveStmt().bind(); - stmt.setString(0, entityId.getEntityType().name()); - stmt.setUUID(1, entityId.getId()); - stmt.setString(2, attributeType); - stmt.setString(3, attribute.getKey()); - stmt.setLong(4, attribute.getLastUpdateTs()); - stmt.setString(5, attribute.getStrValue().orElse(null)); - Optional booleanValue = attribute.getBooleanValue(); - if (booleanValue.isPresent()) { - stmt.setBool(6, booleanValue.get()); - } else { - stmt.setToNull(6); - } - Optional longValue = attribute.getLongValue(); - if (longValue.isPresent()) { - stmt.setLong(7, longValue.get()); - } else { - stmt.setToNull(7); - } - Optional doubleValue = attribute.getDoubleValue(); - if (doubleValue.isPresent()) { - stmt.setDouble(8, doubleValue.get()); - } else { - stmt.setToNull(8); - } + BoundStatement stmt = getSaveStmt().bind() + .setString(0, entityId.getEntityType().name()) + .setUUID(1, entityId.getId()) + .setString(2, attributeType) + .setString(3, attribute.getKey()) + .setLong(4, attribute.getLastUpdateTs()) + .set(5, attribute.getStrValue().orElse(null), String.class) + .set(6, attribute.getBooleanValue().orElse(null), Boolean.class) + .set(7, attribute.getLongValue().orElse(null), Long.class) + .set(8, attribute.getDoubleValue().orElse(null), Double.class) + .set(9, attribute.getJsonValue().orElse(null), String.class); + log.trace("Generated save stmt [{}] for entityId {} and attributeType {} and attribute", stmt, entityId, attributeType, attribute); return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null); } @@ -172,8 +159,9 @@ public class CassandraBaseAttributesDao extends CassandraAbstractAsyncDao implem "," + ModelConstants.BOOLEAN_VALUE_COLUMN + "," + ModelConstants.LONG_VALUE_COLUMN + "," + ModelConstants.DOUBLE_VALUE_COLUMN + + "," + ModelConstants.JSON_VALUE_COLUMN + ")" + - " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"); + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); } return saveStmt; } 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 a07c4868e6..96ce14c459 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 @@ -369,17 +369,18 @@ public class ModelConstants { public static final String STRING_VALUE_COLUMN = "str_v"; public static final String LONG_VALUE_COLUMN = "long_v"; public static final String DOUBLE_VALUE_COLUMN = "dbl_v"; + public static final String JSON_VALUE_COLUMN = "json_v"; - protected static final String[] NONE_AGGREGATION_COLUMNS = new String[]{LONG_VALUE_COLUMN, DOUBLE_VALUE_COLUMN, BOOLEAN_VALUE_COLUMN, STRING_VALUE_COLUMN, KEY_COLUMN, TS_COLUMN}; + 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)}; + 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)}; - protected static final String[] MIN_AGGREGATION_COLUMNS = ArrayUtils.addAll(COUNT_AGGREGATION_COLUMNS, - new String[]{min(LONG_VALUE_COLUMN), min(DOUBLE_VALUE_COLUMN), min(BOOLEAN_VALUE_COLUMN), min(STRING_VALUE_COLUMN)}); - protected static final String[] MAX_AGGREGATION_COLUMNS = ArrayUtils.addAll(COUNT_AGGREGATION_COLUMNS, - new String[]{max(LONG_VALUE_COLUMN), max(DOUBLE_VALUE_COLUMN), max(BOOLEAN_VALUE_COLUMN), max(STRING_VALUE_COLUMN)}); - protected static final String[] SUM_AGGREGATION_COLUMNS = ArrayUtils.addAll(COUNT_AGGREGATION_COLUMNS, - new String[]{sum(LONG_VALUE_COLUMN), sum(DOUBLE_VALUE_COLUMN)}); + protected static final String[] MIN_AGGREGATION_COLUMNS = + ArrayUtils.addAll(COUNT_AGGREGATION_COLUMNS, new String[]{min(LONG_VALUE_COLUMN), min(DOUBLE_VALUE_COLUMN), min(BOOLEAN_VALUE_COLUMN), min(STRING_VALUE_COLUMN), min(JSON_VALUE_COLUMN)}); + protected static final String[] MAX_AGGREGATION_COLUMNS = + ArrayUtils.addAll(COUNT_AGGREGATION_COLUMNS, new String[]{max(LONG_VALUE_COLUMN), max(DOUBLE_VALUE_COLUMN), max(BOOLEAN_VALUE_COLUMN), max(STRING_VALUE_COLUMN), max(JSON_VALUE_COLUMN)}); + protected static final String[] SUM_AGGREGATION_COLUMNS = + ArrayUtils.addAll(COUNT_AGGREGATION_COLUMNS, new String[]{sum(LONG_VALUE_COLUMN), sum(DOUBLE_VALUE_COLUMN)}); protected static final String[] AVG_AGGREGATION_COLUMNS = SUM_AGGREGATION_COLUMNS; public static String min(String s) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractTsKvEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractTsKvEntity.java index d7ffc72a34..f0dd03b5ca 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractTsKvEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractTsKvEntity.java @@ -19,6 +19,7 @@ import lombok.Data; import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry; +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.StringDataEntry; @@ -29,12 +30,12 @@ import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import javax.persistence.Transient; - import java.util.UUID; import static org.thingsboard.server.dao.model.ModelConstants.BOOLEAN_VALUE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.DOUBLE_VALUE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.JSON_VALUE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.LONG_VALUE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.STRING_VALUE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.TS_COLUMN; @@ -68,6 +69,9 @@ public abstract class AbstractTsKvEntity implements ToData { @Column(name = DOUBLE_VALUE_COLUMN) protected Double doubleValue; + @Column(name = JSON_VALUE_COLUMN) + protected String jsonValue; + @Transient protected String strKey; @@ -93,6 +97,8 @@ public abstract class AbstractTsKvEntity implements ToData { kvEntry = new DoubleDataEntry(strKey, doubleValue); } else if (booleanValue != null) { kvEntry = new BooleanDataEntry(strKey, booleanValue); + } else if (jsonValue != null) { + kvEntry = new JsonDataEntry(strKey, jsonValue); } return new BasicTsKvEntry(ts, kvEntry); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java index 250d4325e5..f0de269cea 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry; +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.StringDataEntry; @@ -33,6 +34,7 @@ import java.io.Serializable; import static org.thingsboard.server.dao.model.ModelConstants.BOOLEAN_VALUE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.DOUBLE_VALUE_COLUMN; +import static org.thingsboard.server.dao.model.ModelConstants.JSON_VALUE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.LAST_UPDATE_TS_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.LONG_VALUE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.STRING_VALUE_COLUMN; @@ -57,6 +59,9 @@ public class AttributeKvEntity implements ToData, Serializable @Column(name = DOUBLE_VALUE_COLUMN) private Double doubleValue; + @Column(name = JSON_VALUE_COLUMN) + private String jsonValue; + @Column(name = LAST_UPDATE_TS_COLUMN) private Long lastUpdateTs; @@ -71,7 +76,10 @@ public class AttributeKvEntity implements ToData, Serializable kvEntry = new DoubleDataEntry(id.getAttributeKey(), doubleValue); } else if (longValue != null) { kvEntry = new LongDataEntry(id.getAttributeKey(), longValue); + } else if (jsonValue != null) { + kvEntry = new JsonDataEntry(id.getAttributeKey(), jsonValue); } + return new BaseAttributeKvEntry(kvEntry, lastUpdateTs); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/hsql/TsKvEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/hsql/TsKvEntity.java index ba24543090..a38dc185a8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/hsql/TsKvEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sqlts/hsql/TsKvEntity.java @@ -16,30 +16,16 @@ package org.thingsboard.server.dao.model.sqlts.hsql; import lombok.Data; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.kv.BasicTsKvEntry; -import org.thingsboard.server.common.data.kv.BooleanDataEntry; -import org.thingsboard.server.common.data.kv.DoubleDataEntry; -import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.data.kv.LongDataEntry; -import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.dao.model.ToData; import org.thingsboard.server.dao.model.sql.AbstractTsKvEntity; import javax.persistence.Column; import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.IdClass; import javax.persistence.Table; -import javax.persistence.Transient; -import java.util.UUID; - -import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_ID_COLUMN; -import static org.thingsboard.server.dao.model.ModelConstants.ENTITY_TYPE_COLUMN; import static org.thingsboard.server.dao.model.ModelConstants.KEY_COLUMN; @Data @@ -98,12 +84,14 @@ public final class TsKvEntity extends AbstractTsKvEntity implements ToData PATTERN_THREAD_LOCAL = ThreadLocal.withInitial(() -> Pattern.compile(String.valueOf(Character.MIN_VALUE))); private static final String EMPTY_STR = ""; - private static final String BATCH_UPDATE = "UPDATE attribute_kv SET str_v = ?, long_v = ?, dbl_v = ?, bool_v = ?, last_update_ts = ? " + + private static final String BATCH_UPDATE = "UPDATE attribute_kv SET str_v = ?, long_v = ?, dbl_v = ?, bool_v = ?, json_v = cast(? AS json), last_update_ts = ? " + "WHERE entity_type = ? and entity_id = ? and attribute_type =? and attribute_key = ?;"; private static final String INSERT_OR_UPDATE = - "INSERT INTO attribute_kv (entity_type, entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, last_update_ts) " + - "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) " + + "INSERT INTO attribute_kv (entity_type, entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, json_v, last_update_ts) " + + "VALUES(?, ?, ?, ?, ?, ?, ?, ?, cast(? AS json), ?) " + "ON CONFLICT (entity_type, entity_id, attribute_type, attribute_key) " + - "DO UPDATE SET str_v = ?, long_v = ?, dbl_v = ?, bool_v = ?, last_update_ts = ?;"; - - protected static final String BOOL_V = "bool_v"; - protected static final String STR_V = "str_v"; - protected static final String LONG_V = "long_v"; - protected static final String DBL_V = "dbl_v"; + "DO UPDATE SET str_v = ?, long_v = ?, dbl_v = ?, bool_v = ?, json_v = cast(? AS json), last_update_ts = ?;"; @Autowired protected JdbcTemplate jdbcTemplate; @@ -68,74 +60,6 @@ public abstract class AttributeKvInsertRepository { @Value("${sql.remove_null_chars}") private boolean removeNullChars; - @PersistenceContext - protected EntityManager entityManager; - - public abstract void saveOrUpdate(AttributeKvEntity entity); - - protected void processSaveOrUpdate(AttributeKvEntity entity, String requestBoolValue, String requestStrValue, String requestLongValue, String requestDblValue) { - if (entity.getBooleanValue() != null) { - saveOrUpdateBoolean(entity, requestBoolValue); - } - if (entity.getStrValue() != null) { - saveOrUpdateString(entity, requestStrValue); - } - if (entity.getLongValue() != null) { - saveOrUpdateLong(entity, requestLongValue); - } - if (entity.getDoubleValue() != null) { - saveOrUpdateDouble(entity, requestDblValue); - } - } - - @Modifying - private void saveOrUpdateBoolean(AttributeKvEntity entity, String query) { - entityManager.createNativeQuery(query) - .setParameter("entity_type", entity.getId().getEntityType().name()) - .setParameter("entity_id", entity.getId().getEntityId()) - .setParameter("attribute_type", entity.getId().getAttributeType()) - .setParameter("attribute_key", entity.getId().getAttributeKey()) - .setParameter("bool_v", entity.getBooleanValue()) - .setParameter("last_update_ts", entity.getLastUpdateTs()) - .executeUpdate(); - } - - @Modifying - private void saveOrUpdateString(AttributeKvEntity entity, String query) { - entityManager.createNativeQuery(query) - .setParameter("entity_type", entity.getId().getEntityType().name()) - .setParameter("entity_id", entity.getId().getEntityId()) - .setParameter("attribute_type", entity.getId().getAttributeType()) - .setParameter("attribute_key", entity.getId().getAttributeKey()) - .setParameter("str_v", replaceNullChars(entity.getStrValue())) - .setParameter("last_update_ts", entity.getLastUpdateTs()) - .executeUpdate(); - } - - @Modifying - private void saveOrUpdateLong(AttributeKvEntity entity, String query) { - entityManager.createNativeQuery(query) - .setParameter("entity_type", entity.getId().getEntityType().name()) - .setParameter("entity_id", entity.getId().getEntityId()) - .setParameter("attribute_type", entity.getId().getAttributeType()) - .setParameter("attribute_key", entity.getId().getAttributeKey()) - .setParameter("long_v", entity.getLongValue()) - .setParameter("last_update_ts", entity.getLastUpdateTs()) - .executeUpdate(); - } - - @Modifying - private void saveOrUpdateDouble(AttributeKvEntity entity, String query) { - entityManager.createNativeQuery(query) - .setParameter("entity_type", entity.getId().getEntityType().name()) - .setParameter("entity_id", entity.getId().getEntityId()) - .setParameter("attribute_type", entity.getId().getAttributeType()) - .setParameter("attribute_key", entity.getId().getAttributeKey()) - .setParameter("dbl_v", entity.getDoubleValue()) - .setParameter("last_update_ts", entity.getLastUpdateTs()) - .executeUpdate(); - } - protected void saveOrUpdate(List entities) { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override @@ -164,11 +88,13 @@ public abstract class AttributeKvInsertRepository { ps.setNull(4, Types.BOOLEAN); } - ps.setLong(5, kvEntity.getLastUpdateTs()); - ps.setString(6, kvEntity.getId().getEntityType().name()); - ps.setString(7, kvEntity.getId().getEntityId()); - ps.setString(8, kvEntity.getId().getAttributeType()); - ps.setString(9, kvEntity.getId().getAttributeKey()); + ps.setString(5, replaceNullChars(kvEntity.getJsonValue())); + + ps.setLong(6, kvEntity.getLastUpdateTs()); + ps.setString(7, kvEntity.getId().getEntityType().name()); + ps.setString(8, kvEntity.getId().getEntityId()); + ps.setString(9, kvEntity.getId().getAttributeType()); + ps.setString(10, kvEntity.getId().getAttributeKey()); } @Override @@ -199,35 +125,39 @@ public abstract class AttributeKvInsertRepository { ps.setString(2, kvEntity.getId().getEntityId()); ps.setString(3, kvEntity.getId().getAttributeType()); ps.setString(4, kvEntity.getId().getAttributeKey()); + ps.setString(5, replaceNullChars(kvEntity.getStrValue())); - ps.setString(10, replaceNullChars(kvEntity.getStrValue())); + ps.setString(11, replaceNullChars(kvEntity.getStrValue())); if (kvEntity.getLongValue() != null) { ps.setLong(6, kvEntity.getLongValue()); - ps.setLong(11, kvEntity.getLongValue()); + ps.setLong(12, kvEntity.getLongValue()); } else { ps.setNull(6, Types.BIGINT); - ps.setNull(11, Types.BIGINT); + ps.setNull(12, Types.BIGINT); } if (kvEntity.getDoubleValue() != null) { ps.setDouble(7, kvEntity.getDoubleValue()); - ps.setDouble(12, kvEntity.getDoubleValue()); + ps.setDouble(13, kvEntity.getDoubleValue()); } else { ps.setNull(7, Types.DOUBLE); - ps.setNull(12, Types.DOUBLE); + ps.setNull(13, Types.DOUBLE); } if (kvEntity.getBooleanValue() != null) { ps.setBoolean(8, kvEntity.getBooleanValue()); - ps.setBoolean(13, kvEntity.getBooleanValue()); + ps.setBoolean(14, kvEntity.getBooleanValue()); } else { ps.setNull(8, Types.BOOLEAN); - ps.setNull(13, Types.BOOLEAN); + ps.setNull(14, Types.BOOLEAN); } - ps.setLong(9, kvEntity.getLastUpdateTs()); - ps.setLong(14, kvEntity.getLastUpdateTs()); + ps.setString(9, replaceNullChars(kvEntity.getJsonValue())); + ps.setString(15, replaceNullChars(kvEntity.getJsonValue())); + + ps.setLong(10, kvEntity.getLastUpdateTs()); + ps.setLong(16, kvEntity.getLastUpdateTs()); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/HsqlAttributesInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/HsqlAttributesInsertRepository.java index 8378a4488b..1d7aad384a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/HsqlAttributesInsertRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/HsqlAttributesInsertRepository.java @@ -30,36 +30,16 @@ import java.util.List; @Transactional public class HsqlAttributesInsertRepository extends AttributeKvInsertRepository { - private static final String ON_BOOL_VALUE_UPDATE_SET_NULLS = " attribute_kv.str_v = null, attribute_kv.long_v = null, attribute_kv.dbl_v = null "; - private static final String ON_STR_VALUE_UPDATE_SET_NULLS = " attribute_kv.bool_v = null, attribute_kv.long_v = null, attribute_kv.dbl_v = null "; - private static final String ON_LONG_VALUE_UPDATE_SET_NULLS = " attribute_kv.str_v = null, attribute_kv.bool_v = null, attribute_kv.dbl_v = null "; - private static final String ON_DBL_VALUE_UPDATE_SET_NULLS = " attribute_kv.str_v = null, attribute_kv.long_v = null, attribute_kv.bool_v = null "; - - private static final String INSERT_BOOL_STATEMENT = getInsertOrUpdateString(BOOL_V, ON_BOOL_VALUE_UPDATE_SET_NULLS); - private static final String INSERT_STR_STATEMENT = getInsertOrUpdateString(STR_V, ON_STR_VALUE_UPDATE_SET_NULLS); - private static final String INSERT_LONG_STATEMENT = getInsertOrUpdateString(LONG_V, ON_LONG_VALUE_UPDATE_SET_NULLS); - private static final String INSERT_DBL_STATEMENT = getInsertOrUpdateString(DBL_V, ON_DBL_VALUE_UPDATE_SET_NULLS); - private static final String INSERT_OR_UPDATE = - "MERGE INTO attribute_kv USING(VALUES ?, ?, ?, ?, ?, ?, ?, ?, ?) " + - "A (entity_type, entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, last_update_ts) " + + "MERGE INTO attribute_kv USING(VALUES ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " + + "A (entity_type, entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, json_v, last_update_ts) " + "ON (attribute_kv.entity_type=A.entity_type " + "AND attribute_kv.entity_id=A.entity_id " + "AND attribute_kv.attribute_type=A.attribute_type " + "AND attribute_kv.attribute_key=A.attribute_key) " + - "WHEN MATCHED THEN UPDATE SET attribute_kv.str_v = A.str_v, attribute_kv.long_v = A.long_v, attribute_kv.dbl_v = A.dbl_v, attribute_kv.bool_v = A.bool_v, attribute_kv.last_update_ts = A.last_update_ts " + - "WHEN NOT MATCHED THEN INSERT (entity_type, entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, last_update_ts) " + - "VALUES (A.entity_type, A.entity_id, A.attribute_type, A.attribute_key, A.str_v, A.long_v, A.dbl_v, A.bool_v, A.last_update_ts)"; - - @Override - public void saveOrUpdate(AttributeKvEntity entity) { - processSaveOrUpdate(entity, INSERT_BOOL_STATEMENT, INSERT_STR_STATEMENT, INSERT_LONG_STATEMENT, INSERT_DBL_STATEMENT); - } - - private static String getInsertOrUpdateString(String value, String nullValues) { - return "MERGE INTO attribute_kv USING(VALUES :entity_type, :entity_id, :attribute_type, :attribute_key, :" + value + ", :last_update_ts) A (entity_type, entity_id, attribute_type, attribute_key, " + value + ", last_update_ts) ON (attribute_kv.entity_type=A.entity_type AND attribute_kv.entity_id=A.entity_id AND attribute_kv.attribute_type=A.attribute_type AND attribute_kv.attribute_key=A.attribute_key) WHEN MATCHED THEN UPDATE SET attribute_kv." + value + " = A." + value + ", attribute_kv.last_update_ts = A.last_update_ts," + nullValues + "WHEN NOT MATCHED THEN INSERT (entity_type, entity_id, attribute_type, attribute_key, " + value + ", last_update_ts) VALUES (A.entity_type, A.entity_id, A.attribute_type, A.attribute_key, A." + value + ", A.last_update_ts)"; - } - + "WHEN MATCHED THEN UPDATE SET attribute_kv.str_v = A.str_v, attribute_kv.long_v = A.long_v, attribute_kv.dbl_v = A.dbl_v, attribute_kv.bool_v = A.bool_v, attribute_kv.json_v = A.json_v, attribute_kv.last_update_ts = A.last_update_ts " + + "WHEN NOT MATCHED THEN INSERT (entity_type, entity_id, attribute_type, attribute_key, str_v, long_v, dbl_v, bool_v, json_v, last_update_ts) " + + "VALUES (A.entity_type, A.entity_id, A.attribute_type, A.attribute_key, A.str_v, A.long_v, A.dbl_v, A.bool_v, A.json_v, A.last_update_ts)"; @Override protected void saveOrUpdate(List entities) { @@ -89,7 +69,9 @@ public class HsqlAttributesInsertRepository extends AttributeKvInsertRepository ps.setNull(8, Types.BOOLEAN); } - ps.setLong(9, entity.getLastUpdateTs()); + ps.setString(9, entity.getJsonValue()); + + ps.setLong(10, entity.getLastUpdateTs()); }); }); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java index 85a4541be2..ab9964d3e3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java @@ -128,6 +128,7 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl entity.setDoubleValue(attribute.getDoubleValue().orElse(null)); entity.setLongValue(attribute.getLongValue().orElse(null)); entity.setBooleanValue(attribute.getBooleanValue().orElse(null)); + entity.setJsonValue(attribute.getJsonValue().orElse(null)); return addToQueue(entity); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/PsqlAttributesInsertRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/PsqlAttributesInsertRepository.java index 1f553ada04..020e63cd36 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/PsqlAttributesInsertRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/PsqlAttributesInsertRepository.java @@ -17,7 +17,6 @@ package org.thingsboard.server.dao.sql.attributes; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; -import org.thingsboard.server.dao.model.sql.AttributeKvEntity; import org.thingsboard.server.dao.util.PsqlDao; import org.thingsboard.server.dao.util.SqlDao; @@ -27,22 +26,4 @@ import org.thingsboard.server.dao.util.SqlDao; @Transactional public class PsqlAttributesInsertRepository extends AttributeKvInsertRepository { - private static final String ON_BOOL_VALUE_UPDATE_SET_NULLS = "str_v = null, long_v = null, dbl_v = null"; - private static final String ON_STR_VALUE_UPDATE_SET_NULLS = "bool_v = null, long_v = null, dbl_v = null"; - private static final String ON_LONG_VALUE_UPDATE_SET_NULLS = "str_v = null, bool_v = null, dbl_v = null"; - private static final String ON_DBL_VALUE_UPDATE_SET_NULLS = "str_v = null, long_v = null, bool_v = null"; - - private static final String INSERT_OR_UPDATE_BOOL_STATEMENT = getInsertOrUpdateString(BOOL_V, ON_BOOL_VALUE_UPDATE_SET_NULLS); - private static final String INSERT_OR_UPDATE_STR_STATEMENT = getInsertOrUpdateString(STR_V, ON_STR_VALUE_UPDATE_SET_NULLS); - private static final String INSERT_OR_UPDATE_LONG_STATEMENT = getInsertOrUpdateString(LONG_V , ON_LONG_VALUE_UPDATE_SET_NULLS); - private static final String INSERT_OR_UPDATE_DBL_STATEMENT = getInsertOrUpdateString(DBL_V, ON_DBL_VALUE_UPDATE_SET_NULLS); - - @Override - public void saveOrUpdate(AttributeKvEntity entity) { - processSaveOrUpdate(entity, INSERT_OR_UPDATE_BOOL_STATEMENT, INSERT_OR_UPDATE_STR_STATEMENT, INSERT_OR_UPDATE_LONG_STATEMENT, INSERT_OR_UPDATE_DBL_STATEMENT); - } - - private static String getInsertOrUpdateString(String value, String nullValues) { - return "INSERT INTO attribute_kv (entity_type, entity_id, attribute_type, attribute_key, " + value + ", last_update_ts) VALUES (:entity_type, :entity_id, :attribute_type, :attribute_key, :" + value + ", :last_update_ts) ON CONFLICT (entity_type, entity_id, attribute_type, attribute_key) DO UPDATE SET " + value + " = :" + value + ", last_update_ts = :last_update_ts," + nullValues; - } } \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java index 63893c3a5e..e8cbf5d73b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/AbstractSqlTimeseriesDao.java @@ -253,6 +253,8 @@ public abstract class AbstractSqlTimeseriesDao extends JpaAbstractDaoListeningEx latestEntity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); latestEntity.setLongValue(tsKvEntry.getLongValue().orElse(null)); latestEntity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); + latestEntity.setJsonValue(tsKvEntry.getJsonValue().orElse(null)); + return tsLatestQueue.add(latestEntity); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/HsqlInsertTsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/HsqlInsertTsRepository.java index 2d10f35cd9..d1e5294309 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/HsqlInsertTsRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/HsqlInsertTsRepository.java @@ -37,14 +37,14 @@ import java.util.List; public class HsqlInsertTsRepository extends AbstractInsertRepository implements InsertTsRepository { private static final String INSERT_OR_UPDATE = - "MERGE INTO ts_kv USING(VALUES ?, ?, ?, ?, ?, ?, ?) " + - "T (entity_id, key, ts, bool_v, str_v, long_v, dbl_v) " + + "MERGE INTO ts_kv USING(VALUES ?, ?, ?, ?, ?, ?, ?, ?) " + + "T (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) " + "ON (ts_kv.entity_id=T.entity_id " + "AND ts_kv.key=T.key " + "AND ts_kv.ts=T.ts) " + - "WHEN MATCHED THEN UPDATE SET ts_kv.bool_v = T.bool_v, ts_kv.str_v = T.str_v, ts_kv.long_v = T.long_v, ts_kv.dbl_v = T.dbl_v " + - "WHEN NOT MATCHED THEN INSERT (entity_id, key, ts, bool_v, str_v, long_v, dbl_v) " + - "VALUES (T.entity_id, T.key, T.ts, T.bool_v, T.str_v, T.long_v, T.dbl_v);"; + "WHEN MATCHED THEN UPDATE SET ts_kv.bool_v = T.bool_v, ts_kv.str_v = T.str_v, ts_kv.long_v = T.long_v, ts_kv.dbl_v = T.dbl_v ,ts_kv.json_v = T.json_v " + + "WHEN NOT MATCHED THEN INSERT (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) " + + "VALUES (T.entity_id, T.key, T.ts, T.bool_v, T.str_v, T.long_v, T.dbl_v, T.json_v);"; @Override public void saveOrUpdate(List> entities) { @@ -76,6 +76,8 @@ public class HsqlInsertTsRepository extends AbstractInsertRepository implements } else { ps.setNull(7, Types.DOUBLE); } + + ps.setString(8, tsKvEntity.getJsonValue()); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/TsKvHsqlRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/TsKvHsqlRepository.java index 552d515e44..a7c0effb97 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/TsKvHsqlRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/hsql/TsKvHsqlRepository.java @@ -97,7 +97,8 @@ public interface TsKvHsqlRepository extends CrudRepository :startTs AND tskv.ts <= :endTs") CompletableFuture findCount(@Param("entityId") UUID entityId, @Param("entityKey") int entityKey, diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/HsqlLatestInsertTsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/HsqlLatestInsertTsRepository.java index 931eb86689..65ac6257f0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/HsqlLatestInsertTsRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/HsqlLatestInsertTsRepository.java @@ -36,11 +36,11 @@ import java.util.List; public class HsqlLatestInsertTsRepository extends AbstractInsertRepository implements InsertLatestTsRepository { private static final String INSERT_OR_UPDATE = - "MERGE INTO ts_kv_latest USING(VALUES ?, ?, ?, ?, ?, ?, ?) " + - "T (entity_id, key, ts, bool_v, str_v, long_v, dbl_v) " + + "MERGE INTO ts_kv_latest USING(VALUES ?, ?, ?, ?, ?, ?, ?, ?) " + + "T (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) " + "ON (ts_kv_latest.entity_id=T.entity_id " + "AND ts_kv_latest.key=T.key) " + - "WHEN MATCHED THEN UPDATE SET ts_kv_latest.ts = T.ts, ts_kv_latest.bool_v = T.bool_v, ts_kv_latest.str_v = T.str_v, ts_kv_latest.long_v = T.long_v, ts_kv_latest.dbl_v = T.dbl_v " + + "WHEN MATCHED THEN UPDATE SET ts_kv_latest.ts = T.ts, ts_kv_latest.bool_v = T.bool_v, ts_kv_latest.str_v = T.str_v, ts_kv_latest.long_v = T.long_v, ts_kv_latest.dbl_v = T.dbl_v, ts_kv_latest.json_v = T.json_v " + "WHEN NOT MATCHED THEN INSERT (entity_id, key, ts, bool_v, str_v, long_v, dbl_v) " + "VALUES (T.entity_id, T.key, T.ts, T.bool_v, T.str_v, T.long_v, T.dbl_v);"; @@ -72,6 +72,8 @@ public class HsqlLatestInsertTsRepository extends AbstractInsertRepository imple } else { ps.setNull(7, Types.DOUBLE); } + + ps.setString(8, entities.get(i).getJsonValue()); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/PsqlLatestInsertTsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/PsqlLatestInsertTsRepository.java index 16a4c4ccd8..d367f44620 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/PsqlLatestInsertTsRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/PsqlLatestInsertTsRepository.java @@ -38,12 +38,12 @@ import java.util.List; public class PsqlLatestInsertTsRepository extends AbstractInsertRepository implements InsertLatestTsRepository { private static final String BATCH_UPDATE = - "UPDATE ts_kv_latest SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ? WHERE entity_id = ? and key = ?"; + "UPDATE ts_kv_latest SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?, json_v = cast(? AS json) WHERE entity_id = ? and key = ?"; private static final String INSERT_OR_UPDATE = - "INSERT INTO ts_kv_latest (entity_id, key, ts, bool_v, str_v, long_v, dbl_v) VALUES(?, ?, ?, ?, ?, ?, ?) " + - "ON CONFLICT (entity_id, key) DO UPDATE SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?;"; + "INSERT INTO ts_kv_latest (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) VALUES(?, ?, ?, ?, ?, ?, ?, cast(? AS json)) " + + "ON CONFLICT (entity_id, key) DO UPDATE SET ts = ?, bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?, json_v = cast(? AS json);"; @Override public void saveOrUpdate(List entities) { @@ -76,8 +76,10 @@ public class PsqlLatestInsertTsRepository extends AbstractInsertRepository imple ps.setNull(5, Types.DOUBLE); } - ps.setObject(6, tsKvLatestEntity.getEntityId()); - ps.setInt(7, tsKvLatestEntity.getKey()); + ps.setString(6, replaceNullChars(tsKvLatestEntity.getJsonValue())); + + ps.setObject(7, tsKvLatestEntity.getEntityId()); + ps.setInt(8, tsKvLatestEntity.getKey()); } @Override @@ -106,36 +108,39 @@ public class PsqlLatestInsertTsRepository extends AbstractInsertRepository imple TsKvLatestEntity tsKvLatestEntity = insertEntities.get(i); ps.setObject(1, tsKvLatestEntity.getEntityId()); ps.setInt(2, tsKvLatestEntity.getKey()); + ps.setLong(3, tsKvLatestEntity.getTs()); - ps.setLong(8, tsKvLatestEntity.getTs()); + ps.setLong(9, tsKvLatestEntity.getTs()); if (tsKvLatestEntity.getBooleanValue() != null) { ps.setBoolean(4, tsKvLatestEntity.getBooleanValue()); - ps.setBoolean(9, tsKvLatestEntity.getBooleanValue()); + ps.setBoolean(10, tsKvLatestEntity.getBooleanValue()); } else { ps.setNull(4, Types.BOOLEAN); - ps.setNull(9, Types.BOOLEAN); + ps.setNull(10, Types.BOOLEAN); } ps.setString(5, replaceNullChars(tsKvLatestEntity.getStrValue())); - ps.setString(10, replaceNullChars(tsKvLatestEntity.getStrValue())); - + ps.setString(11, replaceNullChars(tsKvLatestEntity.getStrValue())); if (tsKvLatestEntity.getLongValue() != null) { ps.setLong(6, tsKvLatestEntity.getLongValue()); - ps.setLong(11, tsKvLatestEntity.getLongValue()); + ps.setLong(12, tsKvLatestEntity.getLongValue()); } else { ps.setNull(6, Types.BIGINT); - ps.setNull(11, Types.BIGINT); + ps.setNull(12, Types.BIGINT); } if (tsKvLatestEntity.getDoubleValue() != null) { ps.setDouble(7, tsKvLatestEntity.getDoubleValue()); - ps.setDouble(12, tsKvLatestEntity.getDoubleValue()); + ps.setDouble(13, tsKvLatestEntity.getDoubleValue()); } else { ps.setNull(7, Types.DOUBLE); - ps.setNull(12, Types.DOUBLE); + ps.setNull(13, Types.DOUBLE); } + + ps.setString(8, replaceNullChars(tsKvLatestEntity.getJsonValue())); + ps.setString(14, replaceNullChars(tsKvLatestEntity.getJsonValue())); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/SearchTsKvLatestRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/SearchTsKvLatestRepository.java index 5940a33d31..b5da093b6f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/SearchTsKvLatestRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/SearchTsKvLatestRepository.java @@ -29,8 +29,9 @@ import java.util.UUID; public class SearchTsKvLatestRepository { public static final String FIND_ALL_BY_ENTITY_ID = "findAllByEntityId"; + public static final String FIND_ALL_BY_ENTITY_ID_QUERY = "SELECT ts_kv_latest.entity_id AS entityId, ts_kv_latest.key AS key, ts_kv_dictionary.key AS strKey, ts_kv_latest.str_v AS strValue," + - " ts_kv_latest.bool_v AS boolValue, ts_kv_latest.long_v AS longValue, ts_kv_latest.dbl_v AS doubleValue, ts_kv_latest.ts AS ts FROM ts_kv_latest " + + " ts_kv_latest.bool_v AS boolValue, ts_kv_latest.long_v AS longValue, ts_kv_latest.dbl_v AS doubleValue, ts_kv_latest.json_v AS jsonValue, ts_kv_latest.ts AS ts FROM ts_kv_latest " + "INNER JOIN ts_kv_dictionary ON ts_kv_latest.key = ts_kv_dictionary.key_id WHERE ts_kv_latest.entity_id = cast(:id AS uuid)"; @PersistenceContext diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/TsKvLatestRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/TsKvLatestRepository.java index 9ba59c10ef..5232b88489 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/TsKvLatestRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/latest/TsKvLatestRepository.java @@ -20,11 +20,7 @@ import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestCompositeKey; import org.thingsboard.server.dao.model.sqlts.latest.TsKvLatestEntity; import org.thingsboard.server.dao.util.SqlDao; -import java.util.List; -import java.util.UUID; - @SqlDao public interface TsKvLatestRepository extends CrudRepository { - List findAllByEntityId(UUID entityId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java index f598742713..6d60e843ec 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/JpaPsqlTimeseriesDao.java @@ -104,6 +104,7 @@ public class JpaPsqlTimeseriesDao extends AbstractChunkedAggregationTimeseriesDa entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); entity.setLongValue(tsKvEntry.getLongValue().orElse(null)); entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); + entity.setJsonValue(tsKvEntry.getJsonValue().orElse(null)); PsqlPartition psqlPartition = toPartition(tsKvEntry.getTs()); log.trace("Saving entity: {}", entity); return tsQueue.add(new EntityContainer(entity, psqlPartition.getPartitionDate())); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/PsqlInsertTsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/PsqlInsertTsRepository.java index e9cf5c9b03..caf4528812 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/PsqlInsertTsRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/PsqlInsertTsRepository.java @@ -41,8 +41,8 @@ public class PsqlInsertTsRepository extends AbstractInsertRepository implements private static final String INSERT_INTO_TS_KV = "INSERT INTO ts_kv_"; - private static final String VALUES_ON_CONFLICT_DO_UPDATE = " (entity_id, key, ts, bool_v, str_v, long_v, dbl_v) VALUES (?, ?, ?, ?, ?, ?, ?) " + - "ON CONFLICT (entity_id, key, ts) DO UPDATE SET bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?;"; + private static final String VALUES_ON_CONFLICT_DO_UPDATE = " (entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) VALUES (?, ?, ?, ?, ?, ?, ?, cast(? AS json)) " + + "ON CONFLICT (entity_id, key, ts) DO UPDATE SET bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?, json_v = cast(? AS json);"; @Override public void saveOrUpdate(List> entities) { @@ -61,30 +61,33 @@ public class PsqlInsertTsRepository extends AbstractInsertRepository implements if (tsKvEntity.getBooleanValue() != null) { ps.setBoolean(4, tsKvEntity.getBooleanValue()); - ps.setBoolean(8, tsKvEntity.getBooleanValue()); + ps.setBoolean(9, tsKvEntity.getBooleanValue()); } else { ps.setNull(4, Types.BOOLEAN); - ps.setNull(8, Types.BOOLEAN); + ps.setNull(9, Types.BOOLEAN); } ps.setString(5, replaceNullChars(tsKvEntity.getStrValue())); - ps.setString(9, replaceNullChars(tsKvEntity.getStrValue())); + ps.setString(10, replaceNullChars(tsKvEntity.getStrValue())); if (tsKvEntity.getLongValue() != null) { ps.setLong(6, tsKvEntity.getLongValue()); - ps.setLong(10, tsKvEntity.getLongValue()); + ps.setLong(11, tsKvEntity.getLongValue()); } else { ps.setNull(6, Types.BIGINT); - ps.setNull(10, Types.BIGINT); + ps.setNull(11, Types.BIGINT); } if (tsKvEntity.getDoubleValue() != null) { ps.setDouble(7, tsKvEntity.getDoubleValue()); - ps.setDouble(11, tsKvEntity.getDoubleValue()); + ps.setDouble(12, tsKvEntity.getDoubleValue()); } else { ps.setNull(7, Types.DOUBLE); - ps.setNull(11, Types.DOUBLE); + ps.setNull(12, Types.DOUBLE); + + ps.setString(8, replaceNullChars(tsKvEntity.getJsonValue())); + ps.setString(13, replaceNullChars(tsKvEntity.getJsonValue())); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/TsKvPsqlRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/TsKvPsqlRepository.java index 7b3328f86b..b164046bbc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/TsKvPsqlRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/psql/TsKvPsqlRepository.java @@ -98,7 +98,8 @@ public interface TsKvPsqlRepository extends CrudRepository :startTs AND tskv.ts <= :endTs") CompletableFuture findCount(@Param("entityId") UUID entityId, @Param("entityKey") int entityKey, diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/AggregationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/AggregationRepository.java index 15deb702a7..5a0b9c6a59 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/AggregationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/AggregationRepository.java @@ -36,18 +36,17 @@ public class AggregationRepository { public static final String FIND_SUM = "findSum"; public static final String FIND_COUNT = "findCount"; - public static final String FROM_WHERE_CLAUSE = "FROM tenant_ts_kv tskv WHERE tskv.tenant_id = cast(:tenantId AS uuid) AND tskv.entity_id = cast(:entityId AS uuid) AND tskv.key= cast(:entityKey AS int) AND tskv.ts > :startTs AND tskv.ts <= :endTs GROUP BY tskv.tenant_id, tskv.entity_id, tskv.key, tsBucket ORDER BY tskv.tenant_id, tskv.entity_id, tskv.key, tsBucket"; public static final String FIND_AVG_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, 'AVG' AS aggType "; - public static final String FIND_MAX_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, MAX(COALESCE(tskv.long_v, -9223372036854775807)) AS longValue, MAX(COALESCE(tskv.dbl_v, -1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MAX(tskv.str_v) AS strValue, 'MAX' AS aggType "; + public static final String FIND_MAX_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, MAX(COALESCE(tskv.long_v, -9223372036854775807)) AS longValue, MAX(COALESCE(tskv.dbl_v, -1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MAX(tskv.str_v) AS strValue, MAX(tskv.json_v) AS jsonValue, 'MAX' AS aggType "; - public static final String FIND_MIN_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, MIN(COALESCE(tskv.long_v, 9223372036854775807)) AS longValue, MIN(COALESCE(tskv.dbl_v, 1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MIN(tskv.str_v) AS strValue, 'MIN' AS aggType "; + public static final String FIND_MIN_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, MIN(COALESCE(tskv.long_v, 9223372036854775807)) AS longValue, MIN(COALESCE(tskv.dbl_v, 1.79769E+308)) as doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, MIN(tskv.str_v) AS strValue, MIN(tskv.json_v) AS jsonValue,'MIN' AS aggType "; - public static final String FIND_SUM_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, 'SUM' AS aggType "; + public static final String FIND_SUM_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(COALESCE(tskv.long_v, 0)) AS longValue, SUM(COALESCE(tskv.dbl_v, 0.0)) AS doubleValue, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longCountValue, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleCountValue, null AS strValue, null AS jsonValue, 'SUM' AS aggType "; - public static final String FIND_COUNT_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(CASE WHEN tskv.bool_v IS NULL THEN 0 ELSE 1 END) AS booleanValueCount, SUM(CASE WHEN tskv.str_v IS NULL THEN 0 ELSE 1 END) AS strValueCount, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longValueCount, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleValueCount "; + public static final String FIND_COUNT_QUERY = "SELECT time_bucket(:timeBucket, tskv.ts) AS tsBucket, :timeBucket AS interval, SUM(CASE WHEN tskv.bool_v IS NULL THEN 0 ELSE 1 END) AS booleanValueCount, SUM(CASE WHEN tskv.str_v IS NULL THEN 0 ELSE 1 END) AS strValueCount, SUM(CASE WHEN tskv.long_v IS NULL THEN 0 ELSE 1 END) AS longValueCount, SUM(CASE WHEN tskv.dbl_v IS NULL THEN 0 ELSE 1 END) AS doubleValueCount, SUM(CASE WHEN tskv.json_v IS NULL THEN 0 ELSE 1 END) AS jsonValueCount "; @PersistenceContext private EntityManager entityManager; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleInsertTsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleInsertTsRepository.java index a6fad65fbf..6d863af105 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleInsertTsRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleInsertTsRepository.java @@ -37,8 +37,8 @@ import java.util.List; public class TimescaleInsertTsRepository extends AbstractInsertRepository implements InsertTsRepository { private static final String INSERT_OR_UPDATE = - "INSERT INTO tenant_ts_kv (tenant_id, entity_id, key, ts, bool_v, str_v, long_v, dbl_v) VALUES(?, ?, ?, ?, ?, ?, ?, ?) " + - "ON CONFLICT (tenant_id, entity_id, key, ts) DO UPDATE SET bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?;"; + "INSERT INTO tenant_ts_kv (tenant_id, entity_id, key, ts, bool_v, str_v, long_v, dbl_v, json_v) VALUES(?, ?, ?, ?, ?, ?, ?, ?, cast(? AS json)) " + + "ON CONFLICT (tenant_id, entity_id, key, ts) DO UPDATE SET bool_v = ?, str_v = ?, long_v = ?, dbl_v = ?, json_v = cast(? AS json);"; @Override public void saveOrUpdate(List> entities) { @@ -56,28 +56,31 @@ public class TimescaleInsertTsRepository extends AbstractInsertRepository implem ps.setBoolean(9, tsKvEntity.getBooleanValue()); } else { ps.setNull(5, Types.BOOLEAN); - ps.setNull(9, Types.BOOLEAN); + ps.setNull(10, Types.BOOLEAN); } ps.setString(6, replaceNullChars(tsKvEntity.getStrValue())); - ps.setString(10, replaceNullChars(tsKvEntity.getStrValue())); + ps.setString(11, replaceNullChars(tsKvEntity.getStrValue())); if (tsKvEntity.getLongValue() != null) { ps.setLong(7, tsKvEntity.getLongValue()); - ps.setLong(11, tsKvEntity.getLongValue()); + ps.setLong(12, tsKvEntity.getLongValue()); } else { ps.setNull(7, Types.BIGINT); - ps.setNull(11, Types.BIGINT); + ps.setNull(12, Types.BIGINT); } if (tsKvEntity.getDoubleValue() != null) { ps.setDouble(8, tsKvEntity.getDoubleValue()); - ps.setDouble(12, tsKvEntity.getDoubleValue()); + ps.setDouble(13, tsKvEntity.getDoubleValue()); } else { ps.setNull(8, Types.DOUBLE); - ps.setNull(12, Types.DOUBLE); + ps.setNull(13, Types.DOUBLE); } + + ps.setString(9, replaceNullChars(tsKvEntity.getJsonValue())); + ps.setString(14, replaceNullChars(tsKvEntity.getJsonValue())); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleTimeseriesDao.java index f7e51ef18c..9f8f5c6f74 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/timescale/TimescaleTimeseriesDao.java @@ -174,6 +174,8 @@ public class TimescaleTimeseriesDao extends AbstractSqlTimeseriesDao implements entity.setDoubleValue(tsKvEntry.getDoubleValue().orElse(null)); entity.setLongValue(tsKvEntry.getLongValue().orElse(null)); entity.setBooleanValue(tsKvEntry.getBooleanValue().orElse(null)); + entity.setJsonValue(tsKvEntry.getJsonValue().orElse(null)); + log.trace("Saving entity to timescale db: {}", entity); return tsQueue.add(new EntityContainer(entity, null)); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java index d81ddc3801..6229f68818 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/AggregatePartitionsFunction.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; @@ -41,10 +42,12 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct private static final int DOUBLE_CNT_POS = 1; private static final int BOOL_CNT_POS = 2; private static final int STR_CNT_POS = 3; - private static final int LONG_POS = 4; - private static final int DOUBLE_POS = 5; - private static final int BOOL_POS = 6; - private static final int STR_POS = 7; + private static final int JSON_CNT_POS = 4; + private static final int LONG_POS = 5; + private static final int DOUBLE_POS = 6; + private static final int BOOL_POS = 7; + private static final int STR_POS = 8; + private static final int JSON_POS = 9; private final Aggregation aggregation; private final String key; @@ -72,7 +75,7 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct } } return processAggregationResult(aggResult); - }catch (Exception e){ + } catch (Exception e) { log.error("[{}][{}][{}] Failed to aggregate data", key, ts, aggregation, e); return Optional.empty(); } @@ -85,11 +88,13 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct Double curDValue = null; Boolean curBValue = null; String curSValue = null; + String curJValue = null; long longCount = row.getLong(LONG_CNT_POS); long doubleCount = row.getLong(DOUBLE_CNT_POS); long boolCount = row.getLong(BOOL_CNT_POS); long strCount = row.getLong(STR_CNT_POS); + long jsonCount = row.getLong(JSON_CNT_POS); if (longCount > 0 || doubleCount > 0) { if (longCount > 0) { @@ -111,6 +116,10 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct aggResult.dataType = DataType.STRING; curCount = strCount; curSValue = getStringValue(row); + } else if (jsonCount > 0) { + aggResult.dataType = DataType.JSON; + curCount = jsonCount; + curJValue = getJsonValue(row); } else { return; } @@ -120,9 +129,9 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct } else if (aggregation == Aggregation.AVG || aggregation == Aggregation.SUM) { processAvgOrSumAggregation(aggResult, curCount, curLValue, curDValue); } else if (aggregation == Aggregation.MIN) { - processMinAggregation(aggResult, curLValue, curDValue, curBValue, curSValue); + processMinAggregation(aggResult, curLValue, curDValue, curBValue, curSValue, curJValue); } else if (aggregation == Aggregation.MAX) { - processMaxAggregation(aggResult, curLValue, curDValue, curBValue, curSValue); + processMaxAggregation(aggResult, curLValue, curDValue, curBValue, curSValue, curJValue); } } @@ -136,7 +145,7 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct } } - private void processMinAggregation(AggregationResult aggResult, Long curLValue, Double curDValue, Boolean curBValue, String curSValue) { + private void processMinAggregation(AggregationResult aggResult, Long curLValue, Double curDValue, Boolean curBValue, String curSValue, String curJValue) { if (curDValue != null || curLValue != null) { if (curDValue != null) { aggResult.dValue = aggResult.dValue == null ? curDValue : Math.min(aggResult.dValue, curDValue); @@ -148,10 +157,12 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct aggResult.bValue = aggResult.bValue == null ? curBValue : aggResult.bValue && curBValue; } else if (curSValue != null && (aggResult.sValue == null || curSValue.compareTo(aggResult.sValue) < 0)) { aggResult.sValue = curSValue; + } else if (curJValue != null && (aggResult.jValue == null || curJValue.compareTo(aggResult.jValue) < 0)) { + aggResult.jValue = curJValue; } } - private void processMaxAggregation(AggregationResult aggResult, Long curLValue, Double curDValue, Boolean curBValue, String curSValue) { + private void processMaxAggregation(AggregationResult aggResult, Long curLValue, Double curDValue, Boolean curBValue, String curSValue, String curJValue) { if (curDValue != null || curLValue != null) { if (curDValue != null) { aggResult.dValue = aggResult.dValue == null ? curDValue : Math.max(aggResult.dValue, curDValue); @@ -163,6 +174,8 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct aggResult.bValue = aggResult.bValue == null ? curBValue : aggResult.bValue || curBValue; } else if (curSValue != null && (aggResult.sValue == null || curSValue.compareTo(aggResult.sValue) > 0)) { aggResult.sValue = curSValue; + } else if (curJValue != null && (aggResult.jValue == null || curJValue.compareTo(aggResult.jValue) > 0)) { + aggResult.jValue = curJValue; } } @@ -182,6 +195,14 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct } } + private String getJsonValue(Row row) { + if (aggregation == Aggregation.MIN || aggregation == Aggregation.MAX) { + return row.getString(JSON_POS); + } else { + return null; + } + } + private Long getLongValue(Row row) { if (aggregation == Aggregation.MIN || aggregation == Aggregation.MAX || aggregation == Aggregation.SUM || aggregation == Aggregation.AVG) { @@ -223,7 +244,7 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct if (aggResult.count == 0 || (aggResult.dataType == DataType.DOUBLE && aggResult.dValue == null) || (aggResult.dataType == DataType.LONG && aggResult.lValue == null)) { return Optional.empty(); } else if (aggResult.dataType == DataType.DOUBLE || aggResult.dataType == DataType.LONG) { - if(aggregation == Aggregation.AVG || aggResult.hasDouble) { + if (aggregation == Aggregation.AVG || aggResult.hasDouble) { double sum = Optional.ofNullable(aggResult.dValue).orElse(0.0d) + Optional.ofNullable(aggResult.lValue).orElse(0L); return Optional.of(new BasicTsKvEntry(ts, new DoubleDataEntry(key, aggregation == Aggregation.SUM ? sum : (sum / aggResult.count)))); } else { @@ -235,15 +256,17 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct private Optional processMinOrMaxResult(AggregationResult aggResult) { if (aggResult.dataType == DataType.DOUBLE || aggResult.dataType == DataType.LONG) { - if(aggResult.hasDouble) { + if (aggResult.hasDouble) { double currentD = aggregation == Aggregation.MIN ? Optional.ofNullable(aggResult.dValue).orElse(Double.MAX_VALUE) : Optional.ofNullable(aggResult.dValue).orElse(Double.MIN_VALUE); double currentL = aggregation == Aggregation.MIN ? Optional.ofNullable(aggResult.lValue).orElse(Long.MAX_VALUE) : Optional.ofNullable(aggResult.lValue).orElse(Long.MIN_VALUE); return Optional.of(new BasicTsKvEntry(ts, new DoubleDataEntry(key, aggregation == Aggregation.MIN ? Math.min(currentD, currentL) : Math.max(currentD, currentL)))); } else { return Optional.of(new BasicTsKvEntry(ts, new LongDataEntry(key, aggResult.lValue))); } - } else if (aggResult.dataType == DataType.STRING) { + } else if (aggResult.dataType == DataType.STRING) { return Optional.of(new BasicTsKvEntry(ts, new StringDataEntry(key, aggResult.sValue))); + } else if (aggResult.dataType == DataType.JSON) { + return Optional.of(new BasicTsKvEntry(ts, new JsonDataEntry(key, aggResult.jValue))); } else { return Optional.of(new BasicTsKvEntry(ts, new BooleanDataEntry(key, aggResult.bValue))); } @@ -253,6 +276,7 @@ public class AggregatePartitionsFunction implements com.google.common.base.Funct DataType dataType = null; Boolean bValue = null; String sValue = null; + String jValue = null; Double dValue = null; Long lValue = null; long count = 0; diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java index d0ebb49572..8fc8b4ab8a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/CassandraBaseTimeseriesDao.java @@ -29,6 +29,7 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; @@ -42,6 +43,7 @@ import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.kv.DeleteTsKvQuery; import org.thingsboard.server.common.data.kv.DoubleDataEntry; +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; @@ -337,21 +339,31 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.BOOLEAN)); futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.DOUBLE)); futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.STRING)); + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.JSON)); break; case BOOLEAN: futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.DOUBLE)); futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.LONG)); futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.STRING)); + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.JSON)); break; case DOUBLE: futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.BOOLEAN)); futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.LONG)); futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.STRING)); + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.JSON)); break; case STRING: futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.BOOLEAN)); futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.DOUBLE)); futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.LONG)); + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.JSON)); + break; + case JSON: + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.BOOLEAN)); + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.DOUBLE)); + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.LONG)); + futures.add(saveNull(tenantId, entityId, tsKvEntry, ttl, partition, DataType.STRING)); break; } } @@ -411,6 +423,13 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem .set(5, tsKvEntry.getStrValue().orElse(null), String.class) .set(6, tsKvEntry.getLongValue().orElse(null), Long.class) .set(7, tsKvEntry.getDoubleValue().orElse(null), Double.class); + Optional jsonV = tsKvEntry.getJsonValue(); + if (jsonV.isPresent()) { + stmt.setString(8, tsKvEntry.getJsonValue().get()); + } else { + stmt.setToNull(8); + } + return getFuture(executeAsyncWrite(tenantId, stmt), rs -> null); } @@ -669,7 +688,12 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem if (boolV != null) { kvEntry = new BooleanDataEntry(key, boolV); } else { - log.warn("All values in key-value row are nullable "); + String jsonV = row.get(ModelConstants.JSON_VALUE_COLUMN, String.class); + if (StringUtils.isNoneEmpty(jsonV)) { + kvEntry = new JsonDataEntry(key, jsonV); + } else { + log.warn("All values in key-value row are nullable "); + } } } } @@ -772,8 +796,9 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem "," + ModelConstants.BOOLEAN_VALUE_COLUMN + "," + ModelConstants.STRING_VALUE_COLUMN + "," + ModelConstants.LONG_VALUE_COLUMN + - "," + ModelConstants.DOUBLE_VALUE_COLUMN + ")" + - " VALUES(?, ?, ?, ?, ?, ?, ?, ?)"); + "," + ModelConstants.DOUBLE_VALUE_COLUMN + + "," + ModelConstants.JSON_VALUE_COLUMN + ")" + + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"); } return latestInsertStmt; } @@ -812,7 +837,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem ModelConstants.STRING_VALUE_COLUMN + "," + ModelConstants.BOOLEAN_VALUE_COLUMN + "," + ModelConstants.LONG_VALUE_COLUMN + "," + - ModelConstants.DOUBLE_VALUE_COLUMN + " " + + ModelConstants.DOUBLE_VALUE_COLUMN + "," + + ModelConstants.JSON_VALUE_COLUMN + " " + "FROM " + ModelConstants.TS_KV_LATEST_CF + " " + "WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM + @@ -829,7 +855,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem ModelConstants.STRING_VALUE_COLUMN + "," + ModelConstants.BOOLEAN_VALUE_COLUMN + "," + ModelConstants.LONG_VALUE_COLUMN + "," + - ModelConstants.DOUBLE_VALUE_COLUMN + " " + + ModelConstants.DOUBLE_VALUE_COLUMN + "," + + ModelConstants.JSON_VALUE_COLUMN + " " + "FROM " + ModelConstants.TS_KV_LATEST_CF + " " + "WHERE " + ModelConstants.ENTITY_TYPE_COLUMN + EQUALS_PARAM + "AND " + ModelConstants.ENTITY_ID_COLUMN + EQUALS_PARAM); @@ -847,6 +874,8 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem return ModelConstants.LONG_VALUE_COLUMN; case DOUBLE: return ModelConstants.DOUBLE_VALUE_COLUMN; + case JSON: + return ModelConstants.JSON_VALUE_COLUMN; default: throw new RuntimeException("Not implemented!"); } @@ -856,27 +885,23 @@ public class CassandraBaseTimeseriesDao extends CassandraAbstractAsyncDao implem switch (kvEntry.getDataType()) { case BOOLEAN: Optional booleanValue = kvEntry.getBooleanValue(); - if (booleanValue.isPresent()) { - stmt.setBool(column, booleanValue.get().booleanValue()); - } + booleanValue.ifPresent(b -> stmt.setBool(column, b)); break; case STRING: Optional stringValue = kvEntry.getStrValue(); - if (stringValue.isPresent()) { - stmt.setString(column, stringValue.get()); - } + stringValue.ifPresent(s -> stmt.setString(column, s)); break; case LONG: Optional longValue = kvEntry.getLongValue(); - if (longValue.isPresent()) { - stmt.setLong(column, longValue.get().longValue()); - } + longValue.ifPresent(l -> stmt.setLong(column, l)); break; case DOUBLE: Optional doubleValue = kvEntry.getDoubleValue(); - if (doubleValue.isPresent()) { - stmt.setDouble(column, doubleValue.get().doubleValue()); - } + doubleValue.ifPresent(d -> stmt.setDouble(column, d)); + break; + case JSON: + Optional jsonValue = kvEntry.getJsonValue(); + jsonValue.ifPresent(jsonObject -> stmt.setString(column, jsonObject)); break; } } diff --git a/dao/src/main/resources/cassandra/schema-entities.cql b/dao/src/main/resources/cassandra/schema-entities.cql index e9844f7b1c..de2b088cef 100644 --- a/dao/src/main/resources/cassandra/schema-entities.cql +++ b/dao/src/main/resources/cassandra/schema-entities.cql @@ -410,6 +410,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.attributes_kv_cf ( str_v text, long_v bigint, dbl_v double, + json_v text, last_update_ts bigint, PRIMARY KEY ((entity_type, entity_id, attribute_type), attribute_key) ) WITH compaction = { 'class' : 'LeveledCompactionStrategy' }; diff --git a/dao/src/main/resources/cassandra/schema-ts.cql b/dao/src/main/resources/cassandra/schema-ts.cql index 338b420436..c0f4b74467 100644 --- a/dao/src/main/resources/cassandra/schema-ts.cql +++ b/dao/src/main/resources/cassandra/schema-ts.cql @@ -30,6 +30,7 @@ CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_cf ( str_v text, long_v bigint, dbl_v double, + json_v text, PRIMARY KEY (( entity_type, entity_id, key, partition ), ts) ); @@ -51,5 +52,6 @@ CREATE TABLE IF NOT EXISTS thingsboard.ts_kv_latest_cf ( str_v text, long_v bigint, dbl_v double, + json_v text, PRIMARY KEY (( entity_type, entity_id ), key) ) WITH compaction = { 'class' : 'LeveledCompactionStrategy' }; diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql new file mode 100644 index 0000000000..758aaafb10 --- /dev/null +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -0,0 +1,251 @@ +-- +-- Copyright © 2016-2020 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. +-- + + +CREATE TABLE IF NOT EXISTS admin_settings ( + id varchar(31) NOT NULL CONSTRAINT admin_settings_pkey PRIMARY KEY, + json_value varchar, + key varchar(255) +); + +CREATE TABLE IF NOT EXISTS alarm ( + id varchar(31) NOT NULL CONSTRAINT alarm_pkey PRIMARY KEY, + ack_ts bigint, + clear_ts bigint, + additional_info varchar, + end_ts bigint, + originator_id varchar(31), + originator_type integer, + propagate boolean, + severity varchar(255), + start_ts bigint, + status varchar(255), + tenant_id varchar(31), + propagate_relation_types varchar, + type varchar(255) +); + +CREATE TABLE IF NOT EXISTS asset ( + id varchar(31) NOT NULL CONSTRAINT asset_pkey PRIMARY KEY, + additional_info varchar, + customer_id varchar(31), + name varchar(255), + label varchar(255), + search_text varchar(255), + tenant_id varchar(31), + type varchar(255), + CONSTRAINT asset_name_unq_key UNIQUE (tenant_id, name) +); + +CREATE TABLE IF NOT EXISTS audit_log ( + id varchar(31) NOT NULL CONSTRAINT audit_log_pkey PRIMARY KEY, + tenant_id varchar(31), + customer_id varchar(31), + entity_id varchar(31), + entity_type varchar(255), + entity_name varchar(255), + user_id varchar(31), + user_name varchar(255), + action_type varchar(255), + action_data varchar(1000000), + action_status varchar(255), + action_failure_details varchar(1000000) +); + +CREATE TABLE IF NOT EXISTS attribute_kv ( + entity_type varchar(255), + entity_id varchar(31), + attribute_type varchar(255), + attribute_key varchar(255), + bool_v boolean, + str_v varchar(10000000), + long_v bigint, + dbl_v double precision, + json_v varchar(10000000), + last_update_ts bigint, + CONSTRAINT attribute_kv_pkey PRIMARY KEY (entity_type, entity_id, attribute_type, attribute_key) +); + +CREATE TABLE IF NOT EXISTS component_descriptor ( + id varchar(31) NOT NULL CONSTRAINT component_descriptor_pkey PRIMARY KEY, + actions varchar(255), + clazz varchar UNIQUE, + configuration_descriptor varchar, + name varchar(255), + scope varchar(255), + search_text varchar(255), + type varchar(255) +); + +CREATE TABLE IF NOT EXISTS customer ( + id varchar(31) NOT NULL CONSTRAINT customer_pkey PRIMARY KEY, + additional_info varchar, + address varchar, + address2 varchar, + city varchar(255), + country varchar(255), + email varchar(255), + phone varchar(255), + search_text varchar(255), + state varchar(255), + tenant_id varchar(31), + title varchar(255), + zip varchar(255) +); + +CREATE TABLE IF NOT EXISTS dashboard ( + id varchar(31) NOT NULL CONSTRAINT dashboard_pkey PRIMARY KEY, + configuration varchar(10000000), + assigned_customers varchar(1000000), + search_text varchar(255), + tenant_id varchar(31), + title varchar(255) +); + +CREATE TABLE IF NOT EXISTS device ( + id varchar(31) NOT NULL CONSTRAINT device_pkey PRIMARY KEY, + additional_info varchar, + customer_id varchar(31), + type varchar(255), + name varchar(255), + label varchar(255), + search_text varchar(255), + tenant_id varchar(31), + CONSTRAINT device_name_unq_key UNIQUE (tenant_id, name) +); + +CREATE TABLE IF NOT EXISTS device_credentials ( + id varchar(31) NOT NULL CONSTRAINT device_credentials_pkey PRIMARY KEY, + credentials_id varchar, + credentials_type varchar(255), + credentials_value varchar, + device_id varchar(31), + CONSTRAINT device_credentials_id_unq_key UNIQUE (credentials_id) +); + +CREATE TABLE IF NOT EXISTS event ( + id varchar(31) NOT NULL CONSTRAINT event_pkey PRIMARY KEY, + body varchar(10000000), + entity_id varchar(31), + entity_type varchar(255), + event_type varchar(255), + event_uid varchar(255), + tenant_id varchar(31), + CONSTRAINT event_unq_key UNIQUE (tenant_id, entity_type, entity_id, event_type, event_uid) +); + +CREATE TABLE IF NOT EXISTS relation ( + from_id varchar(31), + from_type varchar(255), + to_id varchar(31), + to_type varchar(255), + relation_type_group varchar(255), + relation_type varchar(255), + additional_info varchar, + CONSTRAINT relation_pkey PRIMARY KEY (from_id, from_type, relation_type_group, relation_type, to_id, to_type) +); + +CREATE TABLE IF NOT EXISTS tb_user ( + id varchar(31) NOT NULL CONSTRAINT tb_user_pkey PRIMARY KEY, + additional_info varchar, + authority varchar(255), + customer_id varchar(31), + email varchar(255) UNIQUE, + first_name varchar(255), + last_name varchar(255), + search_text varchar(255), + tenant_id varchar(31) +); + +CREATE TABLE IF NOT EXISTS tenant ( + id varchar(31) NOT NULL CONSTRAINT tenant_pkey PRIMARY KEY, + additional_info varchar, + address varchar, + address2 varchar, + city varchar(255), + country varchar(255), + email varchar(255), + phone varchar(255), + region varchar(255), + search_text varchar(255), + state varchar(255), + title varchar(255), + zip varchar(255) +); + +CREATE TABLE IF NOT EXISTS user_credentials ( + id varchar(31) NOT NULL CONSTRAINT user_credentials_pkey PRIMARY KEY, + activate_token varchar(255) UNIQUE, + enabled boolean, + password varchar(255), + reset_token varchar(255) UNIQUE, + user_id varchar(31) UNIQUE +); + +CREATE TABLE IF NOT EXISTS widget_type ( + id varchar(31) NOT NULL CONSTRAINT widget_type_pkey PRIMARY KEY, + alias varchar(255), + bundle_alias varchar(255), + descriptor varchar(1000000), + name varchar(255), + tenant_id varchar(31) +); + +CREATE TABLE IF NOT EXISTS widgets_bundle ( + id varchar(31) NOT NULL CONSTRAINT widgets_bundle_pkey PRIMARY KEY, + alias varchar(255), + search_text varchar(255), + tenant_id varchar(31), + title varchar(255) +); + +CREATE TABLE IF NOT EXISTS rule_chain ( + id varchar(31) NOT NULL CONSTRAINT rule_chain_pkey PRIMARY KEY, + additional_info varchar, + configuration varchar(10000000), + name varchar(255), + first_rule_node_id varchar(31), + root boolean, + debug_mode boolean, + search_text varchar(255), + tenant_id varchar(31) +); + +CREATE TABLE IF NOT EXISTS rule_node ( + id varchar(31) NOT NULL CONSTRAINT rule_node_pkey PRIMARY KEY, + rule_chain_id varchar(31), + additional_info varchar, + configuration varchar(10000000), + type varchar(255), + name varchar(255), + debug_mode boolean, + search_text varchar(255) +); + +CREATE TABLE IF NOT EXISTS entity_view ( + id varchar(31) NOT NULL CONSTRAINT entity_view_pkey PRIMARY KEY, + entity_id varchar(31), + entity_type varchar(255), + tenant_id varchar(31), + customer_id varchar(31), + type varchar(255), + name varchar(255), + keys varchar(10000000), + start_ts bigint, + end_ts bigint, + search_text varchar(255), + additional_info varchar +); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index f59b1045bc..55893fc124 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -74,6 +74,7 @@ CREATE TABLE IF NOT EXISTS attribute_kv ( str_v varchar(10000000), long_v bigint, dbl_v double precision, + json_v json, last_update_ts bigint, CONSTRAINT attribute_kv_pkey PRIMARY KEY (entity_type, entity_id, attribute_type, attribute_key) ); diff --git a/dao/src/main/resources/sql/schema-timescale.sql b/dao/src/main/resources/sql/schema-timescale.sql index e8cf0de263..7251d8be4e 100644 --- a/dao/src/main/resources/sql/schema-timescale.sql +++ b/dao/src/main/resources/sql/schema-timescale.sql @@ -25,6 +25,7 @@ CREATE TABLE IF NOT EXISTS tenant_ts_kv ( str_v varchar(10000000), long_v bigint, dbl_v double precision, + json_v json, CONSTRAINT tenant_ts_kv_pkey PRIMARY KEY (tenant_id, entity_id, key, ts) ); @@ -42,5 +43,6 @@ CREATE TABLE IF NOT EXISTS ts_kv_latest ( str_v varchar(10000000), long_v bigint, dbl_v double precision, + json_v json, CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) ); \ No newline at end of file diff --git a/dao/src/main/resources/sql/schema-ts-hsql.sql b/dao/src/main/resources/sql/schema-ts-hsql.sql index c29d7e2ed7..eb053a7a84 100644 --- a/dao/src/main/resources/sql/schema-ts-hsql.sql +++ b/dao/src/main/resources/sql/schema-ts-hsql.sql @@ -24,6 +24,7 @@ CREATE TABLE IF NOT EXISTS ts_kv ( str_v varchar(10000000), long_v bigint, dbl_v double precision, + json_v varchar(10000000), CONSTRAINT ts_kv_pkey PRIMARY KEY (entity_id, key, ts) ); @@ -35,6 +36,7 @@ CREATE TABLE IF NOT EXISTS ts_kv_latest ( str_v varchar(10000000), long_v bigint, dbl_v double precision, + json_v varchar(10000000), CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) ); diff --git a/dao/src/main/resources/sql/schema-ts-psql.sql b/dao/src/main/resources/sql/schema-ts-psql.sql index 465c2d51e3..32b6762c8e 100644 --- a/dao/src/main/resources/sql/schema-ts-psql.sql +++ b/dao/src/main/resources/sql/schema-ts-psql.sql @@ -21,7 +21,8 @@ CREATE TABLE IF NOT EXISTS ts_kv ( bool_v boolean, str_v varchar(10000000), long_v bigint, - dbl_v double precision + dbl_v double precision, + json_v json ) PARTITION BY RANGE (ts); CREATE TABLE IF NOT EXISTS ts_kv_latest ( @@ -32,6 +33,7 @@ CREATE TABLE IF NOT EXISTS ts_kv_latest ( str_v varchar(10000000), long_v bigint, dbl_v double precision, + json_v json, CONSTRAINT ts_kv_latest_pkey PRIMARY KEY (entity_id, key) ); diff --git a/dao/src/test/java/org/thingsboard/server/dao/JpaDaoTestSuite.java b/dao/src/test/java/org/thingsboard/server/dao/JpaDaoTestSuite.java index f4d11b328e..0af8603bf1 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/JpaDaoTestSuite.java +++ b/dao/src/test/java/org/thingsboard/server/dao/JpaDaoTestSuite.java @@ -30,7 +30,7 @@ public class JpaDaoTestSuite { @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/system-data.sql"), + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/system-data.sql"), "sql/drop-all-tables.sql", "sql-test.properties" ); diff --git a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java index caddbabc35..7ebab237a8 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java +++ b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java @@ -30,7 +30,7 @@ public class SqlDaoServiceTestSuite { @ClassRule public static CustomSqlUnit sqlUnit = new CustomSqlUnit( - Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql", "sql/system-test.sql"), + Arrays.asList("sql/schema-ts-hsql.sql", "sql/schema-entities-hsql.sql", "sql/schema-entities-idx.sql", "sql/system-data.sql", "sql/system-test.sql"), "sql/drop-all-tables.sql", "sql-test.properties" ); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetAttributesNode.java index 4db628a7f4..be4e6b4e30 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetAttributesNode.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.google.gson.JsonParseException; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.thingsboard.rule.engine.api.TbContext; @@ -33,6 +34,7 @@ import org.thingsboard.server.common.data.kv.KvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.msg.TbMsg; +import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; @@ -77,7 +79,8 @@ public abstract class TbAbstractGetAttributesNode findEntityIdAsync(TbContext ctx, TbMsg msg); @@ -168,6 +171,12 @@ public abstract class TbAbstractGetAttributesNode Date: Sat, 15 Feb 2020 11:47:52 +0200 Subject: [PATCH 2/9] Introduced SMTP TLS version to default mail service and send email node * added tlsVersion to TbSendEmailNode * added tlsVersion to DefaultMailService * added check tlsVersion for old version --- .../DefaultSystemDataLoaderService.java | 5 +- .../service/mail/DefaultMailService.java | 10 +- .../main/resources/cassandra/system-data.cql | 3 +- dao/src/main/resources/sql/system-data.sql | 3 +- .../rule/engine/mail/TbSendEmailNode.java | 13 +- .../mail/TbSendEmailNodeConfiguration.java | 2 + .../app/admin/outgoing-mail-settings.tpl.html | 6 +- ui/src/app/locale/locale.constant-cs_CZ.json | 2 + ui/src/app/locale/locale.constant-de_DE.json | 2 + ui/src/app/locale/locale.constant-el_GR.json | 2 + ui/src/app/locale/locale.constant-en_US.json | 2 + ui/src/app/locale/locale.constant-es_ES.json | 2 + ui/src/app/locale/locale.constant-fa_IR.json | 2 + ui/src/app/locale/locale.constant-fr_FR.json | 3470 +++++++++-------- ui/src/app/locale/locale.constant-it_IT.json | 2 + ui/src/app/locale/locale.constant-ja_JA.json | 3056 +++++++-------- ui/src/app/locale/locale.constant-ko_KR.json | 2 + ui/src/app/locale/locale.constant-lv_LV.json | 2 + ui/src/app/locale/locale.constant-ru_RU.json | 2 + ui/src/app/locale/locale.constant-tr_TR.json | 2 + ui/src/app/locale/locale.constant-uk_UA.json | 2 + ui/src/app/locale/locale.constant-zh_CN.json | 2 + ui/src/app/locale/locale.constant-zh_TW.json | 2 + 23 files changed, 3324 insertions(+), 3272 deletions(-) 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 beba984077..fd907714fa 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 @@ -106,9 +106,10 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { node.put("smtpHost", "localhost"); node.put("smtpPort", "25"); node.put("timeout", "10000"); - node.put("enableTls", "false"); + node.put("enableTls", false); node.put("username", ""); - node.put("password", ""); //NOSONAR, key used to identify password field (not password value itself) + node.put("password", ""); + node.put("tlsVersion", "TLSv1.2");//NOSONAR, key used to identify password field (not password value itself) mailSettings.setJsonValue(node); adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, mailSettings); } diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index 9916c0203a..9d2ae3beac 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -103,7 +103,11 @@ public class DefaultMailService implements MailService { javaMailProperties.put(MAIL_PROP + protocol + ".port", jsonConfig.get("smtpPort").asText()); javaMailProperties.put(MAIL_PROP + protocol + ".timeout", jsonConfig.get("timeout").asText()); javaMailProperties.put(MAIL_PROP + protocol + ".auth", String.valueOf(StringUtils.isNotEmpty(jsonConfig.get("username").asText()))); - javaMailProperties.put(MAIL_PROP + protocol + ".starttls.enable", jsonConfig.has("enableTls") ? jsonConfig.get("enableTls").asText() : "false"); + boolean enableTls = jsonConfig.has("enableTls") && jsonConfig.get("enableTls").booleanValue(); + javaMailProperties.put(MAIL_PROP + protocol + ".starttls.enable", enableTls); + if (enableTls && jsonConfig.has("tlsVersion") && StringUtils.isNoneEmpty(jsonConfig.get("tlsVersion").asText())) { + javaMailProperties.put(MAIL_PROP + protocol + ".ssl.protocols", jsonConfig.get("tlsVersion").asText()); + } return javaMailProperties; } @@ -213,7 +217,7 @@ public class DefaultMailService implements MailService { } @Override - public void sendAccountLockoutEmail( String lockoutEmail, String email, Integer maxFailedLoginAttempts) throws ThingsboardException { + public void sendAccountLockoutEmail(String lockoutEmail, String email, Integer maxFailedLoginAttempts) throws ThingsboardException { String subject = messages.getMessage("account.lockout.subject", null, Locale.US); Map model = new HashMap(); @@ -244,7 +248,7 @@ public class DefaultMailService implements MailService { } private static String mergeTemplateIntoString(VelocityEngine velocityEngine, String templateLocation, - String encoding, Map model) throws VelocityException { + String encoding, Map model) throws VelocityException { StringWriter result = new StringWriter(); mergeTemplate(velocityEngine, templateLocation, encoding, model, result); diff --git a/dao/src/main/resources/cassandra/system-data.cql b/dao/src/main/resources/cassandra/system-data.cql index 2a30dc80f7..96446449f3 100644 --- a/dao/src/main/resources/cassandra/system-data.cql +++ b/dao/src/main/resources/cassandra/system-data.cql @@ -38,7 +38,8 @@ VALUES ( now ( ), 'mail', '{ "smtpHost": "localhost", "smtpPort": "25", "timeout": "10000", - "enableTls": "false", + "enableTls": false, + "tlsVersion": "TLSv1.2", "username": "", "password": "" }' ); \ No newline at end of file diff --git a/dao/src/main/resources/sql/system-data.sql b/dao/src/main/resources/sql/system-data.sql index 6fe28be73d..f261eb2c04 100644 --- a/dao/src/main/resources/sql/system-data.sql +++ b/dao/src/main/resources/sql/system-data.sql @@ -38,7 +38,8 @@ VALUES ( '1e746126eaaefa6a91992ebcb67fe33', 'mail', '{ "smtpHost": "localhost", "smtpPort": "25", "timeout": "10000", - "enableTls": "false", + "enableTls": false, + "tlsVersion": "TLSv1.2", "username": "", "password": "" }' ); diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java index 0b044ec37c..1c41c2a124 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNode.java @@ -20,8 +20,12 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.MimeMessageHelper; +import org.thingsboard.rule.engine.api.RuleNode; +import org.thingsboard.rule.engine.api.TbContext; +import org.thingsboard.rule.engine.api.TbNode; +import org.thingsboard.rule.engine.api.TbNodeConfiguration; +import org.thingsboard.rule.engine.api.TbNodeException; import org.thingsboard.rule.engine.api.util.TbNodeUtils; -import org.thingsboard.rule.engine.api.*; import org.thingsboard.server.common.data.plugin.ComponentType; import org.thingsboard.server.common.msg.TbMsg; @@ -137,10 +141,13 @@ public class TbSendEmailNode implements TbNode { String protocol = this.config.getSmtpProtocol(); javaMailProperties.put("mail.transport.protocol", protocol); javaMailProperties.put(MAIL_PROP + protocol + ".host", this.config.getSmtpHost()); - javaMailProperties.put(MAIL_PROP + protocol + ".port", this.config.getSmtpPort()+""); - javaMailProperties.put(MAIL_PROP + protocol + ".timeout", this.config.getTimeout()+""); + javaMailProperties.put(MAIL_PROP + protocol + ".port", this.config.getSmtpPort() + ""); + javaMailProperties.put(MAIL_PROP + protocol + ".timeout", this.config.getTimeout() + ""); javaMailProperties.put(MAIL_PROP + protocol + ".auth", String.valueOf(StringUtils.isNotEmpty(this.config.getUsername()))); javaMailProperties.put(MAIL_PROP + protocol + ".starttls.enable", Boolean.valueOf(this.config.isEnableTls()).toString()); + if (this.config.isEnableTls() && StringUtils.isNoneEmpty(this.config.getTlsVersion())) { + javaMailProperties.put(MAIL_PROP + protocol + ".ssl.protocols", this.config.getTlsVersion()); + } return javaMailProperties; } } diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNodeConfiguration.java index e7982b6590..3150b6d7f3 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNodeConfiguration.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/mail/TbSendEmailNodeConfiguration.java @@ -29,6 +29,7 @@ public class TbSendEmailNodeConfiguration implements NodeConfiguration { private String smtpProtocol; private int timeout; private boolean enableTls; + private String tlsVersion; @Override public TbSendEmailNodeConfiguration defaultConfiguration() { @@ -39,6 +40,7 @@ public class TbSendEmailNodeConfiguration implements NodeConfiguration { configuration.setSmtpPort(25); configuration.setTimeout(10000); configuration.setEnableTls(false); + configuration.setTlsVersion("TLSv1.2"); return configuration; } } diff --git a/ui/src/app/admin/outgoing-mail-settings.tpl.html b/ui/src/app/admin/outgoing-mail-settings.tpl.html index 1352fbe345..20f988f866 100644 --- a/ui/src/app/admin/outgoing-mail-settings.tpl.html +++ b/ui/src/app/admin/outgoing-mail-settings.tpl.html @@ -78,8 +78,12 @@
admin.timeout-invalid
- {{ 'admin.enable-tls' | translate }} + + + + diff --git a/ui/src/app/locale/locale.constant-cs_CZ.json b/ui/src/app/locale/locale.constant-cs_CZ.json index 8e8a6b8a2f..59d6fd5ece 100644 --- a/ui/src/app/locale/locale.constant-cs_CZ.json +++ b/ui/src/app/locale/locale.constant-cs_CZ.json @@ -83,6 +83,8 @@ "timeout-required": "Hodnota Časový limit je povinná.", "timeout-invalid": "Tohle nevypadá jako platný časový limit.", "enable-tls": "Povolit TLS", + "tls-version": "Verze TLS", + "enter-tls-version" : "Zadejte verzi TLS", "send-test-mail": "Odeslat testovací zprávu" }, "alarm": { diff --git a/ui/src/app/locale/locale.constant-de_DE.json b/ui/src/app/locale/locale.constant-de_DE.json index d5f43f4ef4..7fb595a882 100644 --- a/ui/src/app/locale/locale.constant-de_DE.json +++ b/ui/src/app/locale/locale.constant-de_DE.json @@ -83,6 +83,8 @@ "timeout-required": "Wartezeit ist erforderlich.", "timeout-invalid": "Das ist keine gültige Wartezeit.", "enable-tls": "TLS aktivieren", + "tls-version" : "TLS-Version", + "enter-tls-version" : "Geben Sie die TLS-Version ein", "send-test-mail": "Test E-Mail senden", "security-settings": "Sicherheitseinstellungen", "password-policy": "Kennwortrichtlinie", diff --git a/ui/src/app/locale/locale.constant-el_GR.json b/ui/src/app/locale/locale.constant-el_GR.json index 9b9783a9ad..7669e705b9 100644 --- a/ui/src/app/locale/locale.constant-el_GR.json +++ b/ui/src/app/locale/locale.constant-el_GR.json @@ -88,6 +88,8 @@ "timeout-required": "Απαιτείται τιμή Timeout.", "timeout-invalid": "Αυτή δε φαίνεται να είναι μια έγκυρη τιμή timeout.", "enable-tls": "Ενεργοποίηση TLS", + "tls-version": "Έκδοση TLS", + "enter-tls-version" : "Εισαγάγετε την έκδοση TLS", "send-test-mail": "Αποστολή δοκιμαστικού μηνύματος", "use-system-mail-settings": "Χρήση των ρυθμίσεων διακομιστή αλληλογραφίας συστήματος", "mail-templates": "Πρότυπα αλληλογραφίας", diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index 48bff7d6cd..cfadab8e1f 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -86,6 +86,8 @@ "timeout-required": "Timeout is required.", "timeout-invalid": "That doesn't look like a valid timeout.", "enable-tls": "Enable TLS", + "tls-version": "TLS version", + "enter-tls-version" : "Enter TLS version", "send-test-mail": "Send test mail", "security-settings": "Security settings", "password-policy": "Password policy", diff --git a/ui/src/app/locale/locale.constant-es_ES.json b/ui/src/app/locale/locale.constant-es_ES.json index 75abc889b3..4b76cd7b70 100644 --- a/ui/src/app/locale/locale.constant-es_ES.json +++ b/ui/src/app/locale/locale.constant-es_ES.json @@ -85,6 +85,8 @@ "timeout-required": "Tiempo de espera es requerido.", "timeout-invalid": "Eso no parece un tiempo de espera válido.", "enable-tls": "Habilitar TLS", + "tls-version": "Versión TLS", + "enter-tls-version" : "Ingrese la versión de TLS", "send-test-mail": "Enviar correo de prueba", "password-policy": "Política de contraseñas", "security-settings": "Configuraciones de seguridad", diff --git a/ui/src/app/locale/locale.constant-fa_IR.json b/ui/src/app/locale/locale.constant-fa_IR.json index 34eee792b9..968769fe29 100644 --- a/ui/src/app/locale/locale.constant-fa_IR.json +++ b/ui/src/app/locale/locale.constant-fa_IR.json @@ -83,6 +83,8 @@ "timeout-required": ".مهلت مورد نياز است", "timeout-invalid": ".مهلت، به نظر نمي آيد معتبر باشد", "enable-tls": "TLS فعال سازي", + "tls-version": "نسخه TLS", + "enter-tls-version" : "نسخه TLS را وارد کنید", "send-test-mail": "ارسال پيام آزمايشي" }, "alarm": { diff --git a/ui/src/app/locale/locale.constant-fr_FR.json b/ui/src/app/locale/locale.constant-fr_FR.json index f234afbd88..c12418652a 100644 --- a/ui/src/app/locale/locale.constant-fr_FR.json +++ b/ui/src/app/locale/locale.constant-fr_FR.json @@ -1,1734 +1,1736 @@ -{ - "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!" - }, - "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": "Continue", - "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" - }, - "admin": { - "base-url": "URL de base", - "base-url-required": "L'URL de base est requise.", - "enable-tls": "Activer TLS", - "general": "Général", - "general-settings": "Paramètres généraux", - "mail-from": "Mail de", - "mail-from-required": "Mail de est requis.", - "outgoing-mail": "courrier sortant", - "outgoing-mail-settings": "Paramètres de courrier sortant", - "send-test-mail": "Envoyer un mail 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." - }, - "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" - }, - "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" - }, - "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" - }, - "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 '{{assetType}}' et dont le nom commence par '{{prefix}}'", - "filter-type-asset-type-description": "Actifs de type '{{assetType}}'", - "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 '{{deviceType}}' et dont le nom commence par '{{prefix}}'", - "filter-type-device-type-description": "Dispositifs de type '{{deviceType}}'", - "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-entity-view-type": "Type de vue d'entité", - "filter-type-entity-view-type-and-name-description": "Vues d'entité de type '{{entityView}}' et dont le nom commence par '{{prefix}}'", - "filter-type-entity-view-type-description": "Vues d'entité de type '{{entityView}}'", - "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é" - }, - "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": "Actif file", - "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'asset a été copié dans le presse-papier", - "import": "Import 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": "Label" - }, - "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-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" - }, - "audit-log": { - "action-data": "Action data", - "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-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": "Login", - "type-logout": "Connectez - Out", - "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": "Non attribué du client", - "type-updated": "Mise à jour", - "user": "Utilisateur" - }, - "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" - }, - "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": "Email", - "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" - }, - "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" - } - }, - "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", - "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-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-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", - "select-customer": "Sélectionner un client", - "select-default-customer": "Sélectionnez le client par défaut", - "title": "Titre", - "title-required": "Le titre est requis." - }, - "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 à affecter 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 affecté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 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 affecté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": "Seulement 50 sont autorisés en tant que valeur de marge horizontale maximale.", - "max-mobile-row-height-message": "Seuls 200 pixels sont autorisés en tant que valeur maximale de hauteur de ligne mobile.", - "max-vertical-margin-message": "Seulement 50 sont autorisés en tant que valeur de marge verticale maximale.", - "min-columns-count-message": "Seul un nombre minimum de 10 colonnes est autorisé.", - "min-horizontal-margin-message": "Seul 0 est autorisé comme valeur de marge horizontale minimale.", - "min-mobile-row-height-message": "Seuls 5 pixels sont autorisés en tant que valeur minimale de hauteur de ligne mobile.", - "min-vertical-margin-message": "Seul 0 est autorisé comme 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'affectation 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" - }, - "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;" - }, - "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" - }, - "device": { - "access-token": "Jeton d'accès", - "access-token-invalid": "La longueur du jeton d'accès doit être comprise entre 1 et 20 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": "Affecter 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 ??en double trouvé '{{alias}}'.
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", - "rsa-key": "Clé publique RSA", - "rsa-key-required": "La clé publique RSA est requise.", - "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" - }, - "dialog": { - "close": "Fermer le dialogue" - }, - "entity": { - "add-alias": "Ajouter un alias d'entité", - "alarm-name-starts-with": "Les actifs 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 Assets 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-type-list-empty": "Aucun type d'entité sélectionné.", - "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}}'", - "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?", - "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é" - }, - "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", - "entity": "Entité", - "error": "erreur", - "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" - }, - "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" - }, - "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", - "locales": { - "de_DE": "Allemand", - "en_US": "Anglais", - "fr_FR": "Français", - "es_ES": "Espagnol", - "it_IT": "Italien", - "ko_KR": "Coréen", - "ru_RU": "Russe", - "zh_CN": "Chinois", - "ja_JA": "Japonaise", - "tr_TR": "Turc", - "fa_IR": "Persane", - "uk_UA": "Ukrainien", - "cs_CZ": "Tchèque", - "el_GR": "Grec", - "lv_LV": "Letton" - } - }, - "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": "Login", - "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)" - }, - "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": "à" - }, - "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" - }, - "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": "Rend 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" - }, - "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", - "description": "Description", - "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." - }, - "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." - }, - "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" - }, - "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-values": "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}}'?", - "remove-widget-type": "Supprimer le type de widget", - "remove-widget-type-text": "Après la confirmation, le type de widget et toutes les données associées deviendront irrécupérables.", - "remove-widget-type-title": "Êtes-vous sûr de vouloir supprimer le type de widget '{{widgetName}}'?", - "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-load-failed-error": "Impossible de charger le type de widget!", - "widget-type-not-found": "Problème de chargement de la configuration du widget.
Le type de widget associé a probablement été supprimé." - }, - "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à.
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" - } - }, - "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" - } - }, - "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" - } -} +{ + "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!" + }, + "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": "Continue", + "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" + }, + "admin": { + "base-url": "URL de base", + "base-url-required": "L'URL de base est requise.", + "enable-tls": "Activer TLS", + "tls-version": "Version TLS", + "enter-tls-version" : "Entrez la version TLS", + "general": "Général", + "general-settings": "Paramètres généraux", + "mail-from": "Mail de", + "mail-from-required": "Mail de est requis.", + "outgoing-mail": "courrier sortant", + "outgoing-mail-settings": "Paramètres de courrier sortant", + "send-test-mail": "Envoyer un mail 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." + }, + "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" + }, + "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" + }, + "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" + }, + "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 '{{assetType}}' et dont le nom commence par '{{prefix}}'", + "filter-type-asset-type-description": "Actifs de type '{{assetType}}'", + "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 '{{deviceType}}' et dont le nom commence par '{{prefix}}'", + "filter-type-device-type-description": "Dispositifs de type '{{deviceType}}'", + "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-entity-view-type": "Type de vue d'entité", + "filter-type-entity-view-type-and-name-description": "Vues d'entité de type '{{entityView}}' et dont le nom commence par '{{prefix}}'", + "filter-type-entity-view-type-description": "Vues d'entité de type '{{entityView}}'", + "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é" + }, + "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": "Actif file", + "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'asset a été copié dans le presse-papier", + "import": "Import 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": "Label" + }, + "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-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" + }, + "audit-log": { + "action-data": "Action data", + "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-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": "Login", + "type-logout": "Connectez - Out", + "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": "Non attribué du client", + "type-updated": "Mise à jour", + "user": "Utilisateur" + }, + "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" + }, + "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": "Email", + "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" + }, + "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" + } + }, + "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", + "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-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-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", + "select-customer": "Sélectionner un client", + "select-default-customer": "Sélectionnez le client par défaut", + "title": "Titre", + "title-required": "Le titre est requis." + }, + "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 à affecter 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 affecté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 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 affecté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": "Seulement 50 sont autorisés en tant que valeur de marge horizontale maximale.", + "max-mobile-row-height-message": "Seuls 200 pixels sont autorisés en tant que valeur maximale de hauteur de ligne mobile.", + "max-vertical-margin-message": "Seulement 50 sont autorisés en tant que valeur de marge verticale maximale.", + "min-columns-count-message": "Seul un nombre minimum de 10 colonnes est autorisé.", + "min-horizontal-margin-message": "Seul 0 est autorisé comme valeur de marge horizontale minimale.", + "min-mobile-row-height-message": "Seuls 5 pixels sont autorisés en tant que valeur minimale de hauteur de ligne mobile.", + "min-vertical-margin-message": "Seul 0 est autorisé comme 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'affectation 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" + }, + "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;" + }, + "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" + }, + "device": { + "access-token": "Jeton d'accès", + "access-token-invalid": "La longueur du jeton d'accès doit être comprise entre 1 et 20 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": "Affecter 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 ??en double trouvé '{{alias}}'.
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", + "rsa-key": "Clé publique RSA", + "rsa-key-required": "La clé publique RSA est requise.", + "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" + }, + "dialog": { + "close": "Fermer le dialogue" + }, + "entity": { + "add-alias": "Ajouter un alias d'entité", + "alarm-name-starts-with": "Les actifs 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 Assets 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-type-list-empty": "Aucun type d'entité sélectionné.", + "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}}'", + "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?", + "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é" + }, + "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", + "entity": "Entité", + "error": "erreur", + "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" + }, + "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" + }, + "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", + "locales": { + "de_DE": "Allemand", + "en_US": "Anglais", + "fr_FR": "Français", + "es_ES": "Espagnol", + "it_IT": "Italien", + "ko_KR": "Coréen", + "ru_RU": "Russe", + "zh_CN": "Chinois", + "ja_JA": "Japonaise", + "tr_TR": "Turc", + "fa_IR": "Persane", + "uk_UA": "Ukrainien", + "cs_CZ": "Tchèque", + "el_GR": "Grec", + "lv_LV": "Letton" + } + }, + "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": "Login", + "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)" + }, + "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": "à" + }, + "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" + }, + "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": "Rend 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" + }, + "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", + "description": "Description", + "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." + }, + "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." + }, + "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" + }, + "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-values": "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}}'?", + "remove-widget-type": "Supprimer le type de widget", + "remove-widget-type-text": "Après la confirmation, le type de widget et toutes les données associées deviendront irrécupérables.", + "remove-widget-type-title": "Êtes-vous sûr de vouloir supprimer le type de widget '{{widgetName}}'?", + "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-load-failed-error": "Impossible de charger le type de widget!", + "widget-type-not-found": "Problème de chargement de la configuration du widget.
Le type de widget associé a probablement été supprimé." + }, + "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à.
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" + } + }, + "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" + } + }, + "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" + } +} diff --git a/ui/src/app/locale/locale.constant-it_IT.json b/ui/src/app/locale/locale.constant-it_IT.json index 1c65d04096..8b53c91983 100644 --- a/ui/src/app/locale/locale.constant-it_IT.json +++ b/ui/src/app/locale/locale.constant-it_IT.json @@ -84,6 +84,8 @@ "timeout-required": "Timeout obbligatorio.", "timeout-invalid": "Timeout non valido.", "enable-tls": "Abilita TLS", + "tls-version" : "Versione TLS", + "enter-tls-version" : "Inserisci la versione TLS", "send-test-mail": "Invia mail di test", "security-settings": "Settaggi di sicurezza", "password-policy": "Politica password", diff --git a/ui/src/app/locale/locale.constant-ja_JA.json b/ui/src/app/locale/locale.constant-ja_JA.json index e1ebd4d6b3..6e60c0ed95 100644 --- a/ui/src/app/locale/locale.constant-ja_JA.json +++ b/ui/src/app/locale/locale.constant-ja_JA.json @@ -1,1528 +1,1530 @@ -{ - "access": { - "unauthorized": "無許可", - "unauthorized-access": "不正アクセス", - "unauthorized-access-text": "このリソースにアクセスするにはサインインする必要があります。", - "access-forbidden": "アクセス禁止", - "access-forbidden-text": "あなたはこの場所へのアクセス権を持っていません!この場所にアクセスしたい場合は、別のユーザーとサインインしてみてください。", - "refresh-token-expired": "セッションが終了しました", - "refresh-token-failed": "セッションをリフレッシュできません" - }, - "action": { - "activate": "アクティブ化する", - "suspend": "サスペンド", - "save": "セーブ", - "saveAs": "名前を付けて保存", - "cancel": "キャンセル", - "ok": "[OK]", - "delete": "削除", - "add": "追加", - "yes": "はい", - "no": "いいえ", - "update": "更新", - "remove": "削除する", - "search": "サーチ", - "clear-search": "検索をクリアする", - "assign": "割り当てます", - "unassign": "割り当て解除", - "share": "シェア", - "make-private": "プライベートにする", - "apply": "適用", - "apply-changes": "変更を適用する", - "edit-mode": "編集モード", - "enter-edit-mode": "編集モードに入る", - "decline-changes": "変更を拒否する", - "close": "閉じる", - "back": "バック", - "run": "走る", - "sign-in": "サインイン!", - "edit": "編集", - "view": "ビュー", - "create": "作成する", - "drag": "ドラッグ", - "refresh": "リフレッシュ", - "undo": "元に戻す", - "copy": "コピー", - "paste": "ペースト", - "copy-reference": "コピーリファレンス", - "paste-reference": "参照貼り付け", - "import": "インポート", - "export": "輸出する", - "share-via": "{{provider}}" - }, - "aggregation": { - "aggregation": "集約", - "function": "データ集約機能", - "limit": "最大値", - "group-interval": "グループ化の間隔", - "min": "分", - "max": "最大", - "avg": "平均", - "sum": "和", - "count": "カウント", - "none": "なし" - }, - "admin": { - "general": "一般", - "general-settings": "一般設定", - "outgoing-mail": "送信メール", - "outgoing-mail-settings": "送信メールの設定", - "system-settings": "システム設定", - "test-mail-sent": "テストメールが正常に送信されました!", - "base-url": "ベースURL", - "base-url-required": "ベースURLは必須です。", - "mail-from": "メール", - "mail-from-required": "メールの送信元が必要です。", - "smtp-protocol": "SMTPプロトコル", - "smtp-host": "SMTPホスト", - "smtp-host-required": "SMTPホストが必要です。", - "smtp-port": "SMTPポート", - "smtp-port-required": "smtpポートを指定する必要があります。", - "smtp-port-invalid": "それは有効なsmtpポートのようには見えません。", - "timeout-msec": "タイムアウト(ミリ秒)", - "timeout-required": "タイムアウトが必要です。", - "timeout-invalid": "それは有効なタイムアウトのようには見えません。", - "enable-tls": "TLSを有効にする", - "send-test-mail": "テストメールを送信する" - }, - "alarm": { - "alarm": "警報", - "alarms": "アラーム", - "select-alarm": "アラームを選択", - "no-alarms-matching": "'{{entity}}'発見されました。", - "alarm-required": "アラームが必要です", - "alarm-status": "アラーム状態", - "search-status": { - "ANY": "どれか", - "ACTIVE": "アクティブ", - "CLEARED": "クリアされた", - "ACK": "承認された", - "UNACK": "未確認の" - }, - "display-status": { - "ACTIVE_UNACK": "アクティブ未確認", - "ACTIVE_ACK": "Active Acknowledged", - "CLEARED_UNACK": "クリアされた未確認のメッセージ", - "CLEARED_ACK": "承認された承認済み" - }, - "no-alarms-prompt": "アラームが見つかりません", - "created-time": "作成時刻", - "type": "タイプ", - "severity": "重大度", - "originator": "創始者", - "originator-type": "発信者タイプ", - "details": "詳細", - "status": "状態", - "alarm-details": "アラームの詳細", - "start-time": "始まる時間", - "end-time": "終了時間", - "ack-time": "確認された時間", - "clear-time": "クリアされた時間", - "severity-critical": "クリティカル", - "severity-major": "メジャー", - "severity-minor": "マイナー", - "severity-warning": "警告", - "severity-indeterminate": "不確定", - "acknowledge": "認める", - "clear": "クリア", - "search": "アラームの検索", - "selected-alarms": "{ count, plural, 1 {1 alarm} other {# alarms} }選択された", - "no-data": "表示するデータがありません", - "polling-interval": "アラームポーリング間隔(秒)", - "polling-interval-required": "アラームのポーリング間隔が必要です。", - "min-polling-interval-message": "少なくとも1秒間のポーリング間隔が許可されます。", - "aknowledge-alarms-title": "{ count, plural, 1 {1 alarm} other {# alarms} }", - "aknowledge-alarms-text": "{ count, plural, 1 {1 alarm} other {# alarms} }?", - "clear-alarms-title": "{ count, plural, 1 {1 alarm} other {# alarms} }", - "clear-alarms-text": "{ count, plural, 1 {1 alarm} other {# alarms} }?" - }, - "alias": { - "add": "エイリアスを追加する", - "edit": "エイリアスを編集する", - "name": "エイリアス名", - "name-required": "エイリアス名は必須です", - "duplicate-alias": "同じ名前のエイリアスは既に存在します。", - "filter-type-single-entity": "単一のエンティティ", - "filter-type-entity-list": "エンティティリスト", - "filter-type-entity-name": "エンティティ名", - "filter-type-state-entity": "ダッシュボード状態からのエンティティ", - "filter-type-state-entity-description": "ダッシュボードの状態パラメータから取得されたエンティティ", - "filter-type-asset-type": "資産の種類", - "filter-type-asset-type-description": "'{{assetType}}'", - "filter-type-asset-type-and-name-description": "'{{assetType}}''{{prefix}}'", - "filter-type-device-type": "デバイスタイプ", - "filter-type-device-type-description": "'{{deviceType}}'", - "filter-type-device-type-and-name-description": "'{{deviceType}}''{{prefix}}'", - "filter-type-relations-query": "関係クエリ", - "filter-type-relations-query-description": "{{entities}}{{relationType}}{{direction}}{{rootEntity}}", - "filter-type-asset-search-query": "資産検索クエリ", - "filter-type-asset-search-query-description": "{{assetTypes}}{{relationType}}{{direction}}{{rootEntity}}", - "filter-type-device-search-query": "デバイス検索クエリ", - "filter-type-device-search-query-description": "{{deviceTypes}}{{relationType}}{{direction}}{{rootEntity}}", - "entity-filter": "エンティティフィルタ", - "resolve-multiple": "複数のエンティティとして解決する", - "filter-type": "フィルタタイプ", - "filter-type-required": "フィルタタイプが必要です。", - "entity-filter-no-entity-matched": "指定されたフィルタに一致するエンティティは見つかりませんでした。", - "no-entity-filter-specified": "エンティティフィルタが指定されていない", - "root-state-entity": "ルートとしてダッシュボードの状態エンティティを使用する", - "root-entity": "ルートエンティティ", - "state-entity-parameter-name": "状態エンティティのパラメータ名", - "default-state-entity": "デフォルト状態エンティティ", - "default-entity-parameter-name": "デフォルトでは", - "max-relation-level": "最大関連レベル", - "unlimited-level": "無制限レベル", - "state-entity": "ダッシュボードの状態エンティティ", - "all-entities": "すべてのエンティティ", - "any-relation": "どれか" - }, - "asset": { - "asset": "資産", - "assets": "資産", - "management": "資産運用管理", - "view-assets": "アセットの表示", - "add": "アセットを追加", - "assign-to-customer": "顧客に割り当てる", - "assign-asset-to-customer": "顧客に資産を割り当てる", - "assign-asset-to-customer-text": "顧客に割り当てる資産を選択してください", - "no-assets-text": "アセットが見つかりません", - "assign-to-customer-text": "資産を割り当てる顧客を選択してください", - "public": "パブリック", - "assignedToCustomer": "顧客に割り当てられた", - "make-public": "アセットを公開する", - "make-private": "アセットをプライベートにする", - "unassign-from-customer": "顧客からの割り当て解除", - "delete": "アセットを削除", - "asset-public": "資産は公開されています", - "asset-type": "資産の種類", - "asset-type-required": "資産の種類が必要です。", - "select-asset-type": "アセットタイプを選択", - "enter-asset-type": "アセットタイプを入力", - "any-asset": "すべてのアセット", - "no-asset-types-matching": "'{{entitySubtype}}'発見されました。", - "asset-type-list-empty": "選択されたアセットタイプはありません。", - "asset-types": "資産タイプ", - "name": "名", - "name-required": "名前は必須です。", - "description": "説明", - "type": "タイプ", - "type-required": "タイプが必要です。", - "details": "詳細", - "events": "イベント", - "add-asset-text": "新しいアセットを追加する", - "asset-details": "資産の詳細", - "assign-assets": "アセットの割り当て", - "assign-assets-text": "{ count, plural, 1 {1 asset} other {# assets} }顧客に", - "delete-assets": "アセットを削除する", - "unassign-assets": "アセットの割り当てを解除する", - "unassign-assets-action-title": "{ count, plural, 1 {1 asset} other {# assets} }顧客から", - "assign-new-asset": "新しいアセットを割り当てる", - "delete-asset-title": "'{{assetName}}'?", - "delete-asset-text": "確認後、資産と関連するすべてのデータが回復不能になることに注意してください。", - "delete-assets-title": "{ count, plural, 1 {1 asset} other {# assets} }?", - "delete-assets-action-title": "{ count, plural, 1 {1 asset} other {# assets} }", - "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 asset} other {# assets} }?", - "unassign-assets-text": "確認後、選択されたすべての資産が割り当て解除され、顧客がアクセスできなくなります。", - "copyId": "アセットIDをコピーする", - "idCopiedMessage": "アセットIDがクリップボードにコピーされました", - "select-asset": "アセットを選択", - "no-assets-matching": "'{{entity}}'発見されました。", - "asset-required": "資産が必要です", - "name-starts-with": "アセット名はで始まります", - "label": "ラベル" - }, - "attribute": { - "attributes": "属性", - "latest-telemetry": "最新テレメトリ", - "attributes-scope": "エンティティ属性のスコープ", - "scope-latest-telemetry": "最新テレメトリ", - "scope-client": "クライアントの属性", - "scope-server": "サーバーの属性", - "scope-shared": "共有属性", - "add": "属性を追加する", - "key": "キー", - "last-update-time": "最終更新時間", - "key-required": "属性キーは必須です。", - "value": "値", - "value-required": "属性値は必須です。", - "delete-attributes-title": "{ count, plural, 1 {1 attribute} other {# attributes} }?", - "delete-attributes-text": "注意してください。確認後、選択したすべての属性が削除されます。", - "delete-attributes": "属性を削除する", - "enter-attribute-value": "属性値を入力", - "show-on-widget": "ウィジェットで表示", - "widget-mode": "ウィジェットモード", - "next-widget": "次のウィジェット", - "prev-widget": "前のウィジェット", - "add-to-dashboard": "ダッシュボードに追加", - "add-widget-to-dashboard": "ウィジェットをダッシュ​​ボードに追加する", - "selected-attributes": "{ count, plural, 1 {1 attribute} other {# attributes} }選択された", - "selected-telemetry": "{ count, plural, 1 {1 telemetry unit} other {# telemetry units} }選択された" - }, - "audit-log": { - "audit": "監査", - "audit-logs": "監査ログ", - "timestamp": "タイムスタンプ", - "entity-type": "エンティティタイプ", - "entity-name": "エンティティ名", - "user": "ユーザー", - "type": "タイプ", - "status": "状態", - "details": "詳細", - "type-added": "追加された", - "type-deleted": "削除済み", - "type-updated": "更新しました", - "type-attributes-updated": "属性が更新されました", - "type-attributes-deleted": "属性が削除されました", - "type-rpc-call": "RPC呼び出し", - "type-credentials-updated": "資格が更新されました", - "type-assigned-to-customer": "顧客に割り当てられた", - "type-unassigned-from-customer": "顧客から割り当てられていない", - "type-activated": "活性化", - "type-suspended": "一時停止中", - "type-credentials-read": "信用証明書を読む", - "type-attributes-read": "読み取られた属性", - "type-relation-add-or-update": "関係が更新されました", - "type-relation-delete": "関係が削除されました", - "type-relations-delete": "すべてのリレーションを削除", - "type-alarm-ack": "承認された", - "type-alarm-clear": "クリアされた", - "status-success": "成功", - "status-failure": "失敗", - "audit-log-details": "監査ログの詳細", - "no-audit-logs-prompt": "ログが見つかりません", - "action-data": "行動データ", - "failure-details": "失敗の詳細", - "search": "監査ログの検索", - "clear-search": "検索をクリアする" - }, - "confirm-on-exit": { - "message": "保存されていない変更があります。あなたは本当にこのページを出るのですか?", - "html-message": "保存していない変更があります。
このページを終了してもよろしいですか?", - "title": "保存されていない変更" - }, - "contact": { - "country": "国", - "city": "シティ", - "state": "州/県", - "postal-code": "郵便番号", - "postal-code-invalid": "無効な郵便番号形式です。", - "address": "住所", - "address2": "アドレス2", - "phone": "電話", - "email": "Eメール", - "no-address": "住所がありません" - }, - "common": { - "username": "ユーザー名", - "password": "パスワード", - "enter-username": "ユーザーネームを入力してください", - "enter-password": "パスワードを入力する", - "enter-search": "検索を入力" - }, - "content-type": { - "json": "Json", - "text": "テキスト", - "binary": "バイナリ(Base64)" - }, - "customer": { - "customer": "顧客", - "customers": "顧客", - "management": "顧客管理", - "dashboard": "カスタマーダッシュボード", - "dashboards": "カスタマーダッシュボード", - "devices": "顧客デバイス", - "assets": "顧客資産", - "public-dashboards": "パブリックダッシュボード", - "public-devices": "パブリックデバイス", - "public-assets": "公的資産", - "add": "顧客を追加", - "delete": "顧客を削除する", - "manage-customer-users": "顧客ユーザーを管理する", - "manage-customer-devices": "顧客のデバイスを管理する", - "manage-customer-dashboards": "顧客ダッシュボードの管理", - "manage-public-devices": "パブリックデバイスを管理する", - "manage-public-dashboards": "公開ダッシュボードの管理", - "manage-customer-assets": "顧客資産の管理", - "manage-public-assets": "公的資産を管理する", - "add-customer-text": "新規顧客を追加", - "no-customers-text": "顧客が見つかりません", - "customer-details": "お客様情報", - "delete-customer-title": "'{{customerTitle}}'?", - "delete-customer-text": "確認後、お客様および関連するすべてのデータが回復不能になるので注意してください。", - "delete-customers-title": "{ count, plural, 1 {1 customer} other {# customers} }?", - "delete-customers-action-title": "{ count, plural, 1 {1 customer} other {# customers} }", - "delete-customers-text": "確認後、選択したすべての顧客は削除され、関連するすべてのデータは回復不能になります。", - "manage-users": "ユーザーを管理する", - "manage-assets": "アセットを管理する", - "manage-devices": "デバイスを管理する", - "manage-dashboards": "ダッシュボードの管理", - "title": "タイトル", - "title-required": "タイトルは必須です。", - "description": "説明", - "details": "詳細", - "events": "イベント", - "copyId": "顧客IDをコピー", - "idCopiedMessage": "顧客IDがクリップボードにコピーされました", - "select-customer": "顧客を選択", - "no-customers-matching": "'{{entity}}'発見されました。", - "customer-required": "顧客は必須です", - "select-default-customer": "デフォルトの顧客を選択", - "default-customer": "デフォルトの顧客", - "default-customer-required": "テナントレベルのダッシュボードをデバッグするには、デフォルトの顧客が必要です" - }, - "datetime": { - "date-from": "デートから", - "time-from": "からの時間", - "date-to": "日付", - "time-to": "の時間" - }, - "dashboard": { - "dashboard": "ダッシュボード", - "dashboards": "ダッシュボード", - "management": "ダッシュボード管理", - "view-dashboards": "ダッシュボードを表示する", - "add": "ダッシュボードを追加", - "assign-dashboard-to-customer": "顧客にダッシュボードを割り当てる", - "assign-dashboard-to-customer-text": "顧客に割り当てるダッシュボードを選択してください", - "assign-to-customer-text": "ダッシュボードを割り当てる顧客を選択してください", - "assign-to-customer": "顧客に割り当てる", - "unassign-from-customer": "顧客からの割り当て解除", - "make-public": "ダッシュボードを公開する", - "make-private": "ダッシュボードを非公開にする", - "manage-assigned-customers": "割り当てられた顧客を管理する", - "assigned-customers": "割り当てられた顧客", - "assign-to-customers": "顧客にダッシュボードを割り当てる", - "assign-to-customers-text": "ダッシュボードを割り当てる顧客を選択してください", - "unassign-from-customers": "顧客からのダッシュボードの割り当て解除", - "unassign-from-customers-text": "ダッシュボードから割り当て解除する顧客を選択してください", - "no-dashboards-text": "ダッシュボードが見つかりません", - "no-widgets": "ウィジェットは設定されていません", - "add-widget": "新しいウィジェットを追加", - "title": "タイトル", - "select-widget-title": "ウィジェットを選択", - "select-widget-subtitle": "利用可能なウィジェットタイプのリスト", - "delete": "ダッシュボードの削除", - "title-required": "タイトルは必須です。", - "description": "説明", - "details": "詳細", - "dashboard-details": "ダッシュボードの詳細", - "add-dashboard-text": "新しいダッシュボードを追加する", - "assign-dashboards": "ダッシュボードの割り当て", - "assign-new-dashboard": "新しいダッシュボードを割り当てる", - "assign-dashboards-text": "{ count, plural, 1 {1 dashboard} other {# dashboards} }顧客に", - "unassign-dashboards-action-text": "{ count, plural, 1 {1 dashboard} other {# dashboards} }顧客から", - "delete-dashboards": "ダッシュボードの削除", - "unassign-dashboards": "ダッシュボードの割り当てを解除する", - "unassign-dashboards-action-title": "{ count, plural, 1 {1 dashboard} other {# dashboards} }顧客から", - "delete-dashboard-title": "'{{dashboardTitle}}'?", - "delete-dashboard-text": "確認後、ダッシュボードとすべての関連データが回復不能になるので注意してください。", - "delete-dashboards-title": "{ count, plural, 1 {1 dashboard} other {# dashboards} }?", - "delete-dashboards-action-title": "{ count, plural, 1 {1 dashboard} other {# dashboards} }", - "delete-dashboards-text": "注意してください。確認後、選択したダッシュボードはすべて削除され、関連するすべてのデータは回復不能になります。", - "unassign-dashboard-title": "'{{dashboardTitle}}'?", - "unassign-dashboard-text": "確認後、ダッシュボードは割り当てられなくなり、顧客はアクセスできなくなります。", - "unassign-dashboard": "ダッシュボードの割り当てを解除する", - "unassign-dashboards-title": "{ count, plural, 1 {1 dashboard} other {# dashboards} }?", - "unassign-dashboards-text": "確認の後、選択したすべてのダッシュボードは割り当てられなくなり、顧客はアクセスできなくなります。", - "public-dashboard-title": "ダッシュボードは公開されました", - "public-dashboard-text": "{{dashboardTitle}} is now public and accessible via next public link:", - "public-dashboard-notice": "注:データにアクセスするために、関連するデバイスを公開することを忘れないでください。", - "make-private-dashboard-title": "'{{dashboardTitle}}'プライベート?", - "make-private-dashboard-text": "確認の後、ダッシュボードはプライベートにされ、他の人がアクセスすることはできません。", - "make-private-dashboard": "ダッシュボードを非公開にする", - "socialshare-text": "'{{dashboardTitle}}'ThingsBoardを搭載", - "socialshare-title": "'{{dashboardTitle}}'ThingsBoardを搭載", - "select-dashboard": "ダッシュボードを選択", - "no-dashboards-matching": "'{{entity}}'発見されました。", - "dashboard-required": "ダッシュボードが必要です。", - "select-existing": "既存のダッシュボードを選択", - "create-new": "新しいダッシュボードを作成する", - "new-dashboard-title": "新しいダッシュボードのタイトル", - "open-dashboard": "ダッシュボードを開く", - "set-background": "背景を設定する", - "background-color": "背景色", - "background-image": "背景画像", - "background-size-mode": "背景サイズモード", - "no-image": "選択した画像がありません", - "drop-image": "画像をドロップするか、クリックしてアップロードするファイルを選択します。", - "settings": "設定", - "columns-count": "列数", - "columns-count-required": "列数が必要です。", - "min-columns-count-message": "わずか10の最小列数が許可されます。", - "max-columns-count-message": "最大1000の列カウントのみが許可されます。", - "widgets-margins": "ウィジェット間のマージン", - "horizontal-margin": "水平マージン", - "horizontal-margin-required": "水平余白値が必要です。", - "min-horizontal-margin-message": "最小水平マージン値としては0だけが許容されます。", - "max-horizontal-margin-message": "最大水平マージン値は50だけです。", - "vertical-margin": "垂直マージン", - "vertical-margin-required": "垂直マージン値が必要です。", - "min-vertical-margin-message": "最小の垂直マージン値として0のみが許可されます。", - "max-vertical-margin-message": "最大垂直マージン値は50のみです。", - "autofill-height": "自動レイアウトの高さ", - "mobile-layout": "モバイルレイアウトの設定", - "mobile-row-height": "モバイル行の高さ、px", - "mobile-row-height-required": "モバイル行の高さ値が必要です。", - "min-mobile-row-height-message": "最小の行の高さの値として、5ピクセルしか許可されません。", - "max-mobile-row-height-message": "移動可能な行の高さの最大値として許可されるのは200ピクセルだけです。", - "display-title": "ダッシュボードのタイトルを表示する", - "toolbar-always-open": "ツールバーを開いたままにする", - "title-color": "タイトルカラー", - "display-dashboards-selection": "ダッシュボードの選択を表示する", - "display-entities-selection": "エンティティの選択を表示する", - "display-dashboard-timewindow": "タイムウィンドウを表示する", - "display-dashboard-export": "エクスポートの表示", - "import": "インポートダッシュボード", - "export": "エクスポートダッシュボード", - "export-failed-error": "{{error}}", - "create-new-dashboard": "新しいダッシュボードを作成する", - "dashboard-file": "ダッシュボードファイル", - "invalid-dashboard-file-error": "ダッシュボードをインポートできません:ダッシュボードのデータ構造が無効です。", - "dashboard-import-missing-aliases-title": "インポートされたダッシュボードで使用されるエイリアスを設定する", - "create-new-widget": "新しいウィジェットを作成する", - "import-widget": "インポートウィジェット", - "widget-file": "ウィジェットファイル", - "invalid-widget-file-error": "ウィジェットをインポートできません:ウィジェットのデータ構造が無効です。", - "widget-import-missing-aliases-title": "インポートされたウィジェットで使用されるエイリアスを設定する", - "open-toolbar": "ダッシュボードツールバーを開く", - "close-toolbar": "ツールバーを閉じる", - "configuration-error": "設定エラー", - "alias-resolution-error-title": "ダッシュボードエイリアス設定エラー", - "invalid-aliases-config": "エイリアスフィルタの一部に一致するデバイスを見つけることができません。
この問題を解決するには、管理者に連絡してください。", - "select-devices": "デバイスの選択", - "assignedToCustomer": "顧客に割り当てられた", - "assignedToCustomers": "顧客に割り当てられた", - "public": "パブリック", - "public-link": "パブリックリンク", - "copy-public-link": "パブリックリンクをコピーする", - "public-link-copied-message": "ダッシュボードのパブリックリンクがクリップボードにコピーされました", - "manage-states": "ダッシュボードの状態を管理する", - "states": "ダッシュボードの状態", - "search-states": "検索ダッシュボードの状態", - "selected-states": "{ count, plural, 1 {1 dashboard state} other {# dashboard states} }選択された", - "edit-state": "ダッシュボードの状態を編集する", - "delete-state": "ダッシュボードの状態を削除する", - "add-state": "ダッシュボードの状態を追加する", - "state": "ダッシュボードの状態", - "state-name": "名", - "state-name-required": "ダッシュボードの状態名は必須です。", - "state-id": "状態ID", - "state-id-required": "ダッシュボードの状態IDは必須です。", - "state-id-exists": "同じIDを持つダッシュボードの状態は既に存在します。", - "is-root-state": "ルート状態", - "delete-state-title": "ダッシュボードの状態を削除する", - "delete-state-text": "'{{stateName}}'?", - "show-details": "詳細を表示", - "hide-details": "詳細を隠す", - "select-state": "ターゲット状態を選択する", - "state-controller": "状態コントローラ" - }, - "datakey": { - "settings": "設定", - "advanced": "上級", - "label": "ラベル", - "color": "色", - "units": "値の隣に表示する特別なシンボル", - "decimals": "浮動小数点の後の桁数", - "data-generation-func": "データ生成関数", - "use-data-post-processing-func": "データ後処理機能を使用する", - "configuration": "データキー設定", - "timeseries": "タイムズ", - "attributes": "属性", - "alarm": "アラームフィールド", - "timeseries-required": "エンティティの時系列データが必要です。", - "timeseries-or-attributes-required": "エンティティのtimeseries /属性は必須です。", - "maximum-timeseries-or-attributes": "{ count, plural, 1 {1 timeseries/attribute is allowed.} other {# timeseries/attributes are allowed} }", - "alarm-fields-required": "アラームフィールドが必要です。", - "function-types": "関数型", - "function-types-required": "関数型が必要です。", - "maximum-function-types": "{ count, plural, 1 {1 function type is allowed.} other {# function types are allowed} }" - }, - "datasource": { - "type": "データソースタイプ", - "name": "名", - "add-datasource-prompt": "データソースを追加してください" - }, - "details": { - "edit-mode": "編集モード", - "toggle-edit-mode": "編集モードを切り替える" - }, - "device": { - "device": "デバイス", - "device-required": "デバイスが必要です。", - "devices": "デバイス", - "management": "端末管理", - "view-devices": "デバイスの表示", - "device-alias": "デバイスエイリアス", - "aliases": "デバイスエイリアス", - "no-alias-matching": "'{{alias}}'見つかりません。", - "no-aliases-found": "別名は見つかりませんでした。", - "no-key-matching": "'{{key}}'見つかりません。", - "no-keys-found": "キーが見つかりません。", - "create-new-alias": "新しいものを作成してください!", - "create-new-key": "新しいものを作成してください!", - "duplicate-alias-error": "'{{alias}}'
デバイスエイリアスは、ダッシュボード内で一意である必要があります。", - "configure-alias": "'{{alias}}'エイリアス", - "no-devices-matching": "'{{entity}}'発見されました。", - "alias": "エイリアス", - "alias-required": "デバイスエイリアスが必要です。", - "remove-alias": "デバイスエイリアスを削除する", - "add-alias": "デバイスエイリアスを追加する", - "name-starts-with": "デバイス名はで始まります", - "device-list": "デバイスリスト", - "use-device-name-filter": "フィルタを使用する", - "device-list-empty": "デバイスが選択されていません。", - "device-name-filter-required": "デバイス名フィルタが必要です。", - "device-name-filter-no-device-matched": "'{{device}}'発見されました。", - "add": "デバイスを追加", - "assign-to-customer": "顧客に割り当てる", - "assign-device-to-customer": "顧客にデバイスを割り当てる", - "assign-device-to-customer-text": "顧客に割り当てるデバイスを選択してください", - "make-public": "端末を公開する", - "make-private": "デバイスを非公開にする", - "no-devices-text": "デバイスが見つかりません", - "assign-to-customer-text": "デバイスを割り当てる顧客を選択してください", - "device-details": "デバイスの詳細", - "add-device-text": "新しいデバイスを追加する", - "credentials": "資格情報", - "manage-credentials": "資格情報を管理する", - "delete": "デバイスを削除する", - "assign-devices": "デバイスを割り当てる", - "assign-devices-text": "{ count, plural, 1 {1 device} other {# devices} }顧客に", - "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": "確認後、デバイスとそのすべてのデータは非公開になり、他人がアクセスできなくなります。", - "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": "デバイス資格情報", - "credentials-type": "資格情報タイプ", - "access-token": "アクセストークン", - "access-token-required": "アクセストークンが必要です。", - "access-token-invalid": "アクセストークンの長さは、1〜20文字でなければなりません。", - "rsa-key": "RSA公開鍵", - "rsa-key-required": "RSA公開鍵が必要です。", - "secret": "秘密", - "secret-required": "秘密が必要です。", - "device-type": "デバイスタイプ", - "device-type-required": "デバイスタイプが必要です。", - "select-device-type": "デバイスタイプを選択", - "enter-device-type": "デバイスタイプを入力", - "any-device": "すべてのデバイス", - "no-device-types-matching": "'{{entitySubtype}}'発見されました。", - "device-type-list-empty": "選択されたデバイスタイプはありません。", - "device-types": "デバイスの種類", - "name": "名", - "name-required": "名前は必須です。", - "description": "説明", - "events": "イベント", - "details": "詳細", - "copyId": "デバイスIDをコピーする", - "copyAccessToken": "コピーアクセストークン", - "idCopiedMessage": "デバイスIDがクリップボードにコピーされました", - "accessTokenCopiedMessage": "デバイスアクセストークンがクリップボードにコピーされました", - "assignedToCustomer": "顧客に割り当てられた", - "unable-delete-device-alias-title": "デバイスエイリアスを削除できません", - "unable-delete-device-alias-text": "'{{deviceAlias}}'{{widgetsList}}", - "is-gateway": "ゲートウェイです", - "public": "パブリック", - "device-public": "デバイスは公開されています", - "select-device": "デバイスの選択" - }, - "dialog": { - "close": "ダイアログを閉じる" - }, - "error": { - "unable-to-connect": "サーバーに接続できません!インターネット接続を確認してください。", - "unhandled-error-code": "{{errorCode}}", - "unknown-error": "不明なエラー" - }, - "entity": { - "entity": "エンティティ", - "entities": "エンティティ", - "aliases": "エンティティエイリアス", - "entity-alias": "エンティティエイリアス", - "unable-delete-entity-alias-title": "エンティティエイリアスを削除できません", - "unable-delete-entity-alias-text": "'{{entityAlias}}'{{widgetsList}}", - "duplicate-alias-error": "'{{alias}}'
エンティティのエイリアスは、ダッシュボード内で一意である必要があります。", - "missing-entity-filter-error": "'{{alias}}'.", - "configure-alias": "'{{alias}}'エイリアス", - "alias": "エイリアス", - "alias-required": "エンティティエイリアスが必要です。", - "remove-alias": "エンティティエイリアスを削除する", - "add-alias": "エンティティエイリアスを追加する", - "entity-list": "エンティティリスト", - "entity-type": "エンティティタイプ", - "entity-types": "エンティティタイプ", - "entity-type-list": "エンティティタイプリスト", - "any-entity": "任意のエンティティ", - "enter-entity-type": "エンティティタイプを入力", - "no-entities-matching": "'{{entity}}'発見されました。", - "no-entity-types-matching": "'{{entityType}}'発見されました。", - "name-starts-with": "名前はで始まる", - "use-entity-name-filter": "フィルタを使用する", - "entity-list-empty": "選択されたエンティティはありません", - "entity-type-list-empty": "エンティティタイプは選択されていません。", - "entity-name-filter-required": "エンティティ名フィルタが必要です。", - "entity-name-filter-no-entity-matched": "'{{entity}}'発見されました。", - "all-subtypes": "すべて", - "select-entities": "エンティティの選択", - "no-aliases-found": "別名は見つかりませんでした。", - "no-alias-matching": "'{{alias}}'見つかりません。", - "create-new-alias": "新しいものを作成してください!", - "key": "キー", - "key-name": "キー名", - "no-keys-found": "キーが見つかりません。", - "no-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": "資産", - "type-assets": "資産", - "list-of-assets": "{ count, plural, 1 {One asset} other {List of # assets} }", - "asset-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}}'", - "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-customer": "顧客", - "type-customers": "顧客", - "list-of-customers": "{ count, plural, 1 {One customer} other {List of # customers} }", - "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": "ダッシュボード", - "type-dashboards": "ダッシュボード", - "list-of-dashboards": "{ count, plural, 1 {One dashboard} other {List of # dashboards} }", - "dashboard-name-starts-with": "'{{prefix}}'", - "type-alarm": "警報", - "type-alarms": "アラーム", - "list-of-alarms": "{ count, plural, 1 {One alarms} other {List of # alarms} }", - "alarm-name-starts-with": "'{{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-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} }選択された", - "entity-name": "エンティティ名", - "details": "エンティティの詳細", - "no-entities-prompt": "エンティティが見つかりません", - "no-data": "表示するデータがありません" - }, - "event": { - "event-type": "イベントタイプ", - "type-error": "エラー", - "type-lc-event": "ライフサイクルイベント", - "type-stats": "統計", - "type-debug-rule-node": "デバッグ", - "type-debug-rule-chain": "デバッグ", - "no-events-prompt": "イベントは見つかりませんでした", - "error": "エラー", - "alarm": "警報", - "event-time": "イベント時間", - "server": "サーバ", - "body": "体", - "method": "方法", - "type": "タイプ", - "entity": "エンティティ", - "message-id": "メッセージID", - "message-type": "メッセージタイプ", - "data-type": "データ・タイプ", - "relation-type": "関係タイプ", - "metadata": "メタデータ", - "data": "データ", - "event": "イベント", - "status": "状態", - "success": "成功", - "failed": "失敗", - "messages-processed": "処理されたメッセージ", - "errors-occurred": "エラーが発生しました" - }, - "extension": { - "extensions": "拡張機能", - "selected-extensions": "{ count, plural, 1 {1 extension} other {# extensions} }選択された", - "type": "タイプ", - "key": "キー", - "value": "値", - "id": "イド", - "extension-id": "内線番号", - "extension-type": "拡張タイプ", - "transformer-json": "JSON *", - "unique-id-required": "現在の拡張IDは既に存在します。", - "delete": "拡張子を削除", - "add": "内線番号を追加", - "edit": "拡張機能を編集する", - "delete-extension-title": "'{{extensionId}}'?", - "delete-extension-text": "確認後、拡張子と関連するすべてのデータが回復不能になることに注意してください。", - "delete-extensions-title": "{ count, plural, 1 {1 extension} other {# extensions} }?", - "delete-extensions-text": "注意してください。確認後、選択したすべての内線番号が削除されます。", - "converters": "コンバーター", - "converter-id": "コンバーターID", - "configuration": "構成", - "converter-configurations": "コンバータ構成", - "token": "セキュリティトークン", - "add-converter": "コンバータを追加する", - "add-config": "コンバータ設定を追加する", - "device-name-expression": "デバイス名式", - "device-type-expression": "デバイスタイプの式", - "custom": "カスタム", - "to-double": "ダブル", - "transformer": "トランス", - "json-required": "トランスフォーマーjsonが必要です。", - "json-parse": "変圧器jsonを解析できません。", - "attributes": "属性", - "add-attribute": "属性を追加する", - "add-map": "マッピング要素を追加する", - "timeseries": "タイムズ", - "add-timeseries": "時系列を追加する", - "field-required": "フィールドは必須項目です", - "brokers": "ブローカー", - "add-broker": "ブローカーを追加", - "host": "ホスト", - "port": "ポート", - "port-range": "ポートは1〜65535の範囲内にある必要があります。", - "ssl": "SSL", - "credentials": "資格情報", - "username": "ユーザー名", - "password": "パスワード", - "retry-interval": "ミリ秒単位の再試行間隔", - "anonymous": "匿名", - "basic": "ベーシック", - "pem": "PEM", - "ca-cert": "CA証明書ファイル*", - "private-key": "秘密鍵ファイル*", - "cert": "証明書ファイル*", - "no-file": "ファイルが選択されていません。", - "drop-file": "ファイルをドロップするか、クリックしてアップロードするファイルを選択します。", - "mapping": "マッピング", - "topic-filter": "トピックフィルタ", - "converter-type": "コンバータタイプ", - "converter-json": "Json", - "json-name-expression": "デバイス名json式", - "topic-name-expression": "デバイス名トピック表現", - "json-type-expression": "デバイスタイプjson式", - "topic-type-expression": "デバイスタイプトピック表現", - "attribute-key-expression": "属性キー式", - "attr-json-key-expression": "属性キーjson式", - "attr-topic-key-expression": "属性キートピック式", - "request-id-expression": "要求ID式", - "request-id-json-expression": "リクエストID json式", - "request-id-topic-expression": "リクエストIDトピック表現", - "response-topic-expression": "応答トピック表現", - "value-expression": "値式", - "topic": "トピック", - "timeout": "タイムアウト(ミリ秒)", - "converter-json-required": "コンバータjsonが必要です。", - "converter-json-parse": "コンバータjsonを解析できません。", - "filter-expression": "フィルタ式", - "connect-requests": "接続要求", - "add-connect-request": "接続要求を追加", - "disconnect-requests": "切断要求", - "add-disconnect-request": "切断リクエストを追加する", - "attribute-requests": "属性要求", - "add-attribute-request": "属性要求を追加する", - "attribute-updates": "属性の更新", - "add-attribute-update": "属性の更新を追加する", - "server-side-rpc": "サーバー側RPC", - "add-server-side-rpc-request": "サーバー側RPC要求を追加する", - "device-name-filter": "デバイス名フィルタ", - "attribute-filter": "属性フィルタ", - "method-filter": "方法フィルター", - "request-topic-expression": "トピック表現を要求する", - "response-timeout": "応答タイムアウト(ミリ秒)", - "topic-expression": "トピック表現", - "client-scope": "クライアントスコープ", - "add-device": "デバイスを追加", - "opc-server": "サーバー", - "opc-add-server": "サーバーを追加", - "opc-add-server-prompt": "サーバーを追加してください", - "opc-application-name": "アプリケーション名", - "opc-application-uri": "アプリケーションURI", - "opc-scan-period-in-seconds": "スキャン時間(秒)", - "opc-security": "セキュリティ", - "opc-identity": "身元", - "opc-keystore": "キーストア", - "opc-type": "タイプ", - "opc-keystore-type": "タイプ", - "opc-keystore-location": "ロケーション*", - "opc-keystore-password": "パスワード", - "opc-keystore-alias": "エイリアス", - "opc-keystore-key-password": "キーのパスワード", - "opc-device-node-pattern": "デバイスノードパターン", - "opc-device-name-pattern": "デバイス名パターン", - "modbus-server": "サーバー/スレーブ", - "modbus-add-server": "サーバー/スレーブを追加する", - "modbus-add-server-prompt": "サーバー/スレーブを追加してください", - "modbus-transport": "輸送", - "modbus-port-name": "シリアルポート名", - "modbus-encoding": "エンコーディング", - "modbus-parity": "パリティ", - "modbus-baudrate": "ボーレート", - "modbus-databits": "データビット", - "modbus-stopbits": "ストップビット", - "modbus-databits-range": "データビットは7〜8の範囲内にある必要があります。", - "modbus-stopbits-range": "ストップビットは1〜2の範囲内でなければなりません。", - "modbus-unit-id": "ユニットID", - "modbus-unit-id-range": "ユニットIDは1〜247の範囲で指定してください。", - "modbus-device-name": "装置名", - "modbus-poll-period": "投票期間(ミリ秒)", - "modbus-attributes-poll-period": "属性のポーリング期間(ミリ秒)", - "modbus-timeseries-poll-period": "時系列ポーリング期間(ミリ秒)", - "modbus-poll-period-range": "投票期間は正の値でなければなりません。", - "modbus-tag": "タグ", - "modbus-function": "関数", - "modbus-register-address": "登録アドレス", - "modbus-register-address-range": "レジスタのアドレスは0〜65535の範囲内である必要があります。", - "modbus-register-bit-index": "ビットインデックス", - "modbus-register-bit-index-range": "ビットインデックスは0〜15の範囲内である必要があります。", - "modbus-register-count": "レジスタ数", - "modbus-register-count-range": "レジスタ数は正の値でなければなりません。", - "modbus-byte-order": "バイト順", - "sync": { - "status": "状態", - "sync": "同期", - "not-sync": "同期しない", - "last-sync-time": "前回の同期時間", - "not-available": "利用不可" - }, - "export-extensions-configuration": "エクステンション設定のエクスポート", - "import-extensions-configuration": "エクステンション設定のインポート", - "import-extensions": "拡張機能のインポート", - "import-extension": "インポート拡張", - "export-extension": "輸出延長", - "file": "拡張機能ファイル", - "invalid-file-error": "無効な拡張ファイル" - }, - "fullscreen": { - "expand": "フルスクリーンに拡大", - "exit": "全画面表示を終了", - "toggle": "フルスクリーンモードを切り替える", - "fullscreen": "全画面表示" - }, - "function": { - "function": "関数" - }, - "grid": { - "delete-item-title": "このアイテムを削除してもよろしいですか?", - "delete-item-text": "注意してください。確認後、この項目と関連するすべてのデータは回復不能になります。", - "delete-items-title": "{ count, plural, 1 {1 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": "トップにスクロールします" - }, - "help": { - "goto-help-page": "ヘルプページに行く" - }, - "home": { - "home": "ホーム", - "profile": "プロフィール", - "logout": "ログアウト", - "menu": "メニュー", - "avatar": "アバター", - "open-user-menu": "ユーザーメニューを開く" - }, - "import": { - "no-file": "ファイルが選択されていません", - "drop-file": "JSONファイルをドロップするか、アップロードするファイルをクリックして選択します。" - }, - "item": { - "selected": "選択された" - }, - "js-func": { - "no-return-error": "関数は値を返す必要があります!", - "return-type-mismatch": "'{{type}}'タイプ!", - "tidy": "きちんとした" - }, - "key-val": { - "key": "キー", - "value": "値", - "remove-entry": "エントリを削除", - "add-entry": "エントリを追加", - "no-data": "エントリなし" - }, - "layout": { - "layout": "レイアウト", - "manage": "レイアウトの管理", - "settings": "レイアウト設定", - "color": "色", - "main": "メイン", - "right": "右", - "select": "ターゲットレイアウトを選択" - }, - "legend": { - "position": "伝説の位置", - "show-max": "最大値を表示", - "show-min": "最小値を表示する", - "show-avg": "平均値を表示", - "show-total": "合計値を表示", - "settings": "凡例の設定", - "min": "分", - "max": "最大", - "avg": "平均", - "total": "合計" - }, - "login": { - "login": "ログイン", - "request-password-reset": "リクエストパスワードのリセット", - "reset-password": "パスワードを再設定する", - "create-password": "パスワードの作成", - "passwords-mismatch-error": "入力されたパスワードは同じでなければなりません!", - "password-again": "パスワードをもう一度", - "sign-in": "サインインしてください", - "username": "ユーザー名(電子メール)", - "remember-me": "私を覚えてますか", - "forgot-password": "パスワードをお忘れですか?", - "password-reset": "パスワードのリセット", - "new-password": "新しいパスワード", - "new-password-again": "新しいパスワードを再入力", - "password-link-sent-message": "パスワードリセットリンクが正常に送信されました!", - "email": "Eメール" - }, - "position": { - "top": "上", - "bottom": "ボトム", - "left": "左", - "right": "右" - }, - "profile": { - "profile": "プロフィール", - "change-password": "パスワードを変更する", - "current-password": "現在のパスワード" - }, - "relation": { - "relations": "関係", - "direction": "方向", - "search-direction": { - "FROM": "から", - "TO": "に" - }, - "direction-type": { - "FROM": "から", - "TO": "に" - }, - "from-relations": "アウトバウンド関係", - "to-relations": "インバウンド関係", - "selected-relations": "{ count, plural, 1 {1 relation} other {# relations} }選択された", - "type": "タイプ", - "to-entity-type": "エンティティタイプへ", - "to-entity-name": "エンティティ名に", - "from-entity-type": "エンティティタイプから", - "from-entity-name": "エンティティ名から", - "to-entity": "実体へ", - "from-entity": "エンティティから", - "delete": "関係を削除する", - "relation-type": "関係タイプ", - "relation-type-required": "関係タイプが必要です。", - "any-relation-type": "いかなるタイプ", - "add": "関係を追加する", - "edit": "関係を編集する", - "delete-to-relation-title": "'{{entityName}}'?", - "delete-to-relation-text": "'{{entityName}}'現在のエンティティとは無関係です。", - "delete-to-relations-title": "{ count, plural, 1 {1 relation} other {# relations} }?", - "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を解析できません。" - }, - "rulechain": { - "rulechain": "ルールチェーン", - "rulechains": "ルールチェーン", - "root": "ルート", - "delete": "ルールチェーンの削除", - "name": "名", - "name-required": "名前は必須です。", - "description": "説明", - "add": "ルールチェーンを追加する", - "set-root": "ルールチェーンのルートを作る", - "set-root-rulechain-title": "'{{ruleChainName}}'ルート?", - "set-root-rulechain-text": "確認後、ルールチェーンはルートになり、すべての受信トランスポートメッセージを処理します。", - "delete-rulechain-title": "'{{ruleChainName}}'?", - "delete-rulechain-text": "確認後、ルールチェーンと関連するすべてのデータが回復不能になるので注意してください。", - "delete-rulechains-title": "{ count, plural, 1 {1 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": "イベント", - "system": "システム", - "import": "ルールチェーンのインポート", - "export": "ルールチェーンのエクスポート", - "export-failed-error": "{{error}}", - "create-new-rulechain": "新しいルールチェーンを作成する", - "rulechain-file": "ルールチェーンファイル", - "invalid-rulechain-file-error": "ルールチェーンをインポートできません:ルールチェーンのデータ構造が無効です。", - "copyId": "ルールチェーンIDのコピー", - "idCopiedMessage": "ルールチェーンIDがクリップボードにコピーされました", - "select-rulechain": "ルールチェーンの選択", - "no-rulechains-matching": "'{{entity}}'発見されました。", - "rulechain-required": "ルールチェーンが必要です", - "management": "ルール管理", - "debug-mode": "デバッグモード" - }, - "rulenode": { - "details": "詳細", - "events": "イベント", - "search": "検索ノード", - "open-node-library": "オープンノードライブラリ", - "add": "ルールノードを追加する", - "name": "名", - "name-required": "名前は必須です。", - "type": "タイプ", - "description": "説明", - "delete": "ルールノードを削除", - "select-all-objects": "すべてのノードと接続を選択する", - "deselect-all-objects": "すべてのノードと接続の選択を解除する", - "delete-selected-objects": "選択したノードと接続を削除する", - "delete-selected": "選択を削除します", - "select-all": "すべて選択", - "copy-selected": "選択したコピー", - "deselect-all": "すべての選択を解除", - "rulenode-details": "ルールノードの詳細", - "debug-mode": "デバッグモード", - "configuration": "構成", - "link": "リンク", - "link-details": "ルールノードのリンクの詳細", - "add-link": "リンクを追加", - "link-label": "リンクラベル", - "link-label-required": "リンクラベルが必要です。", - "custom-link-label": "カスタムリンクラベル", - "custom-link-label-required": "カスタムリンクラベルが必要です。", - "link-labels": "リンクラベル", - "link-labels-required": "リンクラベルが必要です。", - "no-link-labels-found": "リンクラベルが見つかりません", - "no-link-label-matching": "'{{label}}'見つかりません。", - "create-new-link-label": "新しいものを作成してください!", - "type-filter": "フィルタ", - "type-filter-details": "設定された条件で着信メッセージをフィルタリングする", - "type-enrichment": "豊かな", - "type-enrichment-details": "メッセージメタデータに追加情報を追加する", - "type-transformation": "変換", - "type-transformation-details": "メッセージペイロードとメタデータの変更", - "type-action": "アクション", - "type-action-details": "特別なアクションを実行する", - "type-external": "外部", - "type-external-details": "外部システムとの相互作用", - "type-rule-chain": "ルールチェーン", - "type-rule-chain-details": "受信したメッセージを指定したルールチェーンに転送する", - "type-input": "入力", - "type-input-details": "ルールチェーンの論理入力、次の関連ルールノードへの着信メッセージの転送", - "type-unknown": "未知の", - "type-unknown-details": "未解決のルールノード", - "directive-is-not-loaded": "'{{directiveName}}'利用できません。", - "ui-resources-load-error": "構成UIリソースをロードできませんでした。", - "invalid-target-rulechain": "ターゲットルールチェーンを解決できません!", - "test-script-function": "テストスクリプト機能", - "message": "メッセージ", - "message-type": "メッセージタイプ", - "select-message-type": "メッセージタイプを選択", - "message-type-required": "メッセージタイプは必須です", - "metadata": "メタデータ", - "metadata-required": "メタデータのエントリを空にすることはできません。", - "output": "出力", - "test": "テスト", - "help": "助けて" - }, - "tenant": { - "tenant": "テナント", - "tenants": "テナント", - "management": "テナント管理", - "add": "テナントを追加", - "admins": "管理者", - "manage-tenant-admins": "テナント管理者の管理", - "delete": "テナントの削除", - "add-tenant-text": "新しいテナントを追加する", - "no-tenants-text": "テナントは見つかりませんでした", - "tenant-details": "テナントの詳細", - "delete-tenant-title": "'{{tenantTitle}}'?", - "delete-tenant-text": "確認後、テナントと関連するすべてのデータが回復不能になるので注意してください。", - "delete-tenants-title": "{ count, plural, 1 {1 tenant} other {# tenants} }?", - "delete-tenants-action-title": "{ count, plural, 1 {1 tenant} other {# tenants} }", - "delete-tenants-text": "注意してください。確認後、選択されたすべてのテナントが削除され、関連するすべてのデータは回復不能になります。", - "title": "タイトル", - "title-required": "タイトルは必須です。", - "description": "説明", - "details": "詳細", - "events": "イベント", - "copyId": "テナントIDをコピーする", - "idCopiedMessage": "テナントIDがクリップボードにコピーされました", - "select-tenant": "テナントを選択", - "no-tenants-matching": "'{{entity}}'発見されました。", - "tenant-required": "テナントが必要です" - }, - "timeinterval": { - "seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }", - "minutes-interval": "{ minutes, plural, 1 {1 minute} other {# minutes} }", - "hours-interval": "{ hours, plural, 1 {1 hour} other {# hours} }", - "days-interval": "{ days, plural, 1 {1 day} other {# days} }", - "days": "日々", - "hours": "時間", - "minutes": "分", - "seconds": "秒", - "advanced": "上級" - }, - "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": "リアルタイム", - "history": "歴史", - "last-prefix": "最終", - "period": "{{ startTime }}{{ endTime }}", - "edit": "タイムウィンドウを編集", - "date-range": "期間", - "last": "最終", - "time-period": "期間" - }, - "user": { - "user": "ユーザー", - "users": "ユーザー", - "customer-users": "顧客ユーザー", - "tenant-admins": "テナント管理者", - "sys-admin": "システム管理者", - "tenant-admin": "テナント管理者", - "customer": "顧客", - "anonymous": "匿名", - "add": "ユーザーを追加する", - "delete": "ユーザーを削除", - "add-user-text": "新しいユーザーを追加", - "no-users-text": "ユーザが見つかりませんでした", - "user-details": "ユーザーの詳細", - "delete-user-title": "'{{userEmail}}'?", - "delete-user-text": "確認後、ユーザーと関連するすべてのデータが回復不能になるので注意してください。", - "delete-users-title": "{ count, plural, 1 {1 user} other {# users} }?", - "delete-users-action-title": "{ count, plural, 1 {1 user} other {# users} }", - "delete-users-text": "注意してください。確認後、選択したすべてのユーザーが削除され、関連するすべてのデータは回復不能になります。", - "activation-email-sent-message": "アクティベーション電子メールが正常に送信されました!", - "resend-activation": "アクティブ化を再送", - "email": "Eメール", - "email-required": "電子メールが必要です。", - "invalid-email-format": "メールフォーマットが無効です。", - "first-name": "ファーストネーム", - "last-name": "苗字", - "description": "説明", - "default-dashboard": "デフォルトのダッシュボード", - "always-fullscreen": "常に全画面表示", - "select-user": "ユーザーを選択", - "no-users-matching": "'{{entity}}'発見されました。", - "user-required": "ユーザーは必須です", - "activation-method": "起動方法", - "display-activation-link": "アクティブ化リンクを表示する", - "send-activation-mail": "アクティベーションメールを送信する", - "activation-link": "ユーザーアクティベーションリンク", - "activation-link-text": "activation link :", - "copy-activation-link": "アクティブ化リンクをコピーする", - "activation-link-copied-message": "ユーザーのアクティベーションリンクがクリップボードにコピーされました", - "details": "詳細" - }, - "value": { - "type": "値のタイプ", - "string": "文字列", - "string-value": "文字列値", - "integer": "整数", - "integer-value": "整数値", - "invalid-integer-value": "整数値が無効です", - "double": "ダブル", - "double-value": "二重価値", - "boolean": "ブール", - "boolean-value": "ブール値", - "false": "偽", - "true": "真", - "long": "長いです" - }, - "widget": { - "widget-library": "ウィジェットライブラリ", - "widget-bundle": "ウィジェットバンドル", - "select-widgets-bundle": "ウィジェットのバンドルを選択", - "management": "ウィジェット管理", - "editor": "ウィジェットエディタ", - "widget-type-not-found": "ウィジェットの設定を読み込む際に問題が発生しました。
おそらく関連付けられているウィジェットのタイプが削除されています。", - "widget-type-load-error": "次のエラーのためにウィジェットが読み込まれませんでした:", - "remove": "ウィジェットを削除", - "edit": "ウィジェットの編集", - "remove-widget-title": "'{{widgetTitle}}'?", - "remove-widget-text": "確認後、ウィジェットと関連するすべてのデータは回復不能になります。", - "timeseries": "時系列", - "search-data": "検索データ", - "no-data-found": "何もデータが見つかりませんでした", - "latest-values": "最新の値", - "rpc": "コントロールウィジェット", - "alarm": "アラームウィジェット", - "static": "静的ウィジェット", - "select-widget-type": "ウィジェットタイプを選択", - "missing-widget-title-error": "ウィジェットのタイトルを指定する必要があります!", - "widget-saved": "ウィジェットが保存されました", - "unable-to-save-widget-error": "ウィジェットを保存できません!ウィジェットにエラーがあります!", - "save": "ウィジェットを保存", - "saveAs": "ウィジェットを次のように保存する", - "save-widget-type-as": "ウィジェットタイプを次のように保存します", - "save-widget-type-as-text": "新しいウィジェットのタイトルを入力したり、ターゲットウィジェットのバンドルを選択してください", - "toggle-fullscreen": "フルスクリーン切り替え", - "run": "ウィジェットを実行する", - "title": "ウィジェットのタイトル", - "title-required": "ウィジェットのタイトルが必要です。", - "type": "ウィジェットタイプ", - "resources": "リソース", - "resource-url": "JavaScript / CSS URL", - "remove-resource": "リソースを削除する", - "add-resource": "リソースを追加", - "html": "HTML", - "tidy": "きちんとした", - "css": "CSS", - "settings-schema": "設定スキーマ", - "datakey-settings-schema": "データキー設定のスキーマ", - "javascript": "Javascript", - "remove-widget-type-title": "'{{widgetName}}'?", - "remove-widget-type-text": "確認後、ウィジェットのタイプと関連するすべてのデータは回復不能になります。", - "remove-widget-type": "ウィジェットタイプを削除", - "add-widget-type": "新しいウィジェットタイプを追加する", - "widget-type-load-failed-error": "ウィジェットタイプの読み込みに失敗しました!", - "widget-template-load-failed-error": "ウィジェットテンプレートを読み込めませんでした!", - "add": "ウィジェットを追加", - "undo": "ウィジェットの変更を元に戻す", - "export": "ウィジェットの書き出し" - }, - "widget-action": { - "header-button": "ウィジェットのヘッダーボタン", - "open-dashboard-state": "新しいダッシュボードの状態に移動する", - "update-dashboard-state": "現在のダッシュボードの状態を更新する", - "open-dashboard": "他のダッシュボードに移動する", - "custom": "カスタムアクション", - "target-dashboard-state": "ターゲットダッシュボードの状態", - "target-dashboard-state-required": "ターゲットダッシュボードの状態が必要です", - "set-entity-from-widget": "エンティティをウィジェットから設定する", - "target-dashboard": "ターゲットダッシュボード", - "open-right-layout": "右ダッシュボードレイアウトを開く(モバイルビュー)" - }, - "widgets-bundle": { - "current": "現在のバンドル", - "widgets-bundles": "ウィジェットバンドル", - "add": "ウィジェットのバンドルを追加", - "delete": "ウィジェットのバンドルを削除する", - "title": "タイトル", - "title-required": "タイトルは必須です。", - "add-widgets-bundle-text": "新しいウィジェットのバンドルを追加する", - "no-widgets-bundles-text": "ウィジェットバンドルが見つかりません", - "empty": "ウィジェットのバンドルが空です", - "details": "詳細", - "widgets-bundle-details": "ウィジェットのバンドルの詳細", - "delete-widgets-bundle-title": "'{{widgetsBundleTitle}}'?", - "delete-widgets-bundle-text": "確認後、ウィジェットはバンドルされ、関連するすべてのデータは回復不能になります。", - "delete-widgets-bundles-title": "{ count, plural, 1 {1 widgets bundle} other {# widgets bundles} }?", - "delete-widgets-bundles-action-title": "{ count, plural, 1 {1 widgets bundle} other {# widgets bundles} }", - "delete-widgets-bundles-text": "確認後、選択したすべてのウィジェットバンドルは削除され、関連するすべてのデータは回復不能になります。", - "no-widgets-bundles-matching": "'{{widgetsBundle}}'発見されました。", - "widgets-bundle-required": "ウィジェットバンドルが必要です。", - "system": "システム", - "import": "インポートウィジェットバンドル", - "export": "ウィジェットのエクスポートバンドル", - "export-failed-error": "{{error}}", - "create-new-widgets-bundle": "新しいウィジェットバンドルを作成する", - "widgets-bundle-file": "ウィジェットのバンドルファイル", - "invalid-widgets-bundle-file-error": "ウィジェットをインポートできません。bundle:データ構造が無効です。" - }, - "widget-config": { - "data": "データ", - "settings": "設定", - "advanced": "上級", - "title": "タイトル", - "general-settings": "一般設定", - "display-title": "タイトルを表示", - "drop-shadow": "影を落とす", - "enable-fullscreen": "フルスクリーンを有効にする", - "background-color": "背景色", - "text-color": "テキストの色", - "padding": "パディング", - "margin": "マージン", - "widget-style": "ウィジェットスタイル", - "title-style": "タイトルスタイル", - "mobile-mode-settings": "モバイルモードの設定", - "order": "注文", - "height": "高さ", - "units": "値の隣に表示する特別なシンボル", - "decimals": "浮動小数点の後の桁数", - "timewindow": "タイムウィンドウ", - "use-dashboard-timewindow": "ダッシュボードのタイムウィンドウを使用する", - "display-legend": "伝説を表示", - "datasources": "データソース", - "maximum-datasources": "{ count, plural, 1 {1 datasource is allowed.} other {# datasources are allowed} }", - "datasource-type": "タイプ", - "datasource-parameters": "パラメーター", - "remove-datasource": "データソースを削除", - "add-datasource": "データソースを追加", - "target-device": "ターゲットデバイス", - "alarm-source": "アラームソース", - "actions": "行動", - "action": "アクション", - "add-action": "アクションを追加", - "search-actions": "検索アクション", - "action-source": "アクションソース", - "action-source-required": "アクションソースが必要です。", - "action-name": "名", - "action-name-required": "アクション名は必須です。", - "action-name-not-unique": "同じ名前の別のアクションがすでに存在します。
アクション名は、同じアクションソース内で一意である必要があります。", - "action-icon": "アイコン", - "action-type": "タイプ", - "action-type-required": "アクションタイプが必要です。", - "edit-action": "アクションの編集", - "delete-action": "アクションの削除", - "delete-action-title": "ウィジェットアクションを削除する", - "delete-action-text": "'{{actionName}}'?" - }, - "widget-type": { - "import": "インポートウィジェットタイプ", - "export": "ウィジェットのタイプをエクスポートする", - "export-failed-error": "{{error}}", - "create-new-widget-type": "新しいウィジェットタイプを作成する", - "widget-type-file": "ウィジェットタイプファイル", - "invalid-widget-type-file-error": "ウィジェットタイプをインポートできません:ウィジェットタイプのデータ構造が無効です。" - }, - "widgets": { - "date-range-navigator": { - "localizationMap": { - "Sun": "日", - "Mon": "月", - "Tue": "火", - "Wed": "水", - "Thu": "木", - "Fri": "金", - "Sat": "土", - "Jan": "1月", - "Feb": "2月", - "Mar": "3月", - "Apr": "4月", - "May": "5月", - "Jun": "6月", - "Jul": "7月", - "Aug": "8月", - "Sep": "9月", - "Oct": "10月", - "Nov": "11月", - "Dec": "12月", - "January": "1月", - "February": "2月", - "March": "行進", - "April": "4月", - "June": "六月", - "July": "7月", - "August": "8月", - "September": "9月", - "October": "10月", - "November": "11月", - "December": "12月", - "Custom Date Range": "カスタム期間", - "Date Range Template": "日付範囲テンプレート", - "Today": "今日", - "Yesterday": "昨日", - "This Week": "今週", - "Last Week": "先週", - "This Month": "今月", - "Last Month": "先月", - "Year": "年", - "This Year": "今年", - "Last Year": "昨年", - "Date picker": "日付ピッカー", - "Hour": "時", - "Day": "日", - "Week": "週間", - "2 weeks": "2週間", - "Month": "月", - "3 months": "3ヶ月", - "6 months": "6ヵ月", - "Custom interval": "カスタム間隔", - "Interval": "間隔", - "Step size": "刻み幅", - "Ok": "Ok" - } - } - }, - "icon": { - "icon": "アイコン", - "select-icon": "選択アイコン", - "material-icons": "マテリアルアイコン", - "show-all": "すべてのアイコンを表示する" - }, - "custom": { - "widget-action": { - "action-cell-button": "アクションセルボタン", - "row-click": "行のクリック", - "polygon-click": "ポリゴンクリック", - "marker-click": "マーカークリック", - "tooltip-tag-action": "ツールチップのタグアクション" - } - }, - "language": { - "language": "言語", - "locales": { - "de_DE": "ドイツ語", - "fr_FR": "フランス語", - "en_US": "英語", - "ko_KR": "韓国語", - "it_IT": "イタリアの", - "zh_CN": "中国語", - "ru_RU": "ロシア", - "es_ES": "スペイン語", - "ja_JA": "日本語", - "tr_TR": "トルコ語", - "fa_IR": "ペルシャ語", - "uk_UA": "ウクライナ語", - "cs_CZ": "チェコ語で" - } - } +{ + "access": { + "unauthorized": "無許可", + "unauthorized-access": "不正アクセス", + "unauthorized-access-text": "このリソースにアクセスするにはサインインする必要があります。", + "access-forbidden": "アクセス禁止", + "access-forbidden-text": "あなたはこの場所へのアクセス権を持っていません!この場所にアクセスしたい場合は、別のユーザーとサインインしてみてください。", + "refresh-token-expired": "セッションが終了しました", + "refresh-token-failed": "セッションをリフレッシュできません" + }, + "action": { + "activate": "アクティブ化する", + "suspend": "サスペンド", + "save": "セーブ", + "saveAs": "名前を付けて保存", + "cancel": "キャンセル", + "ok": "[OK]", + "delete": "削除", + "add": "追加", + "yes": "はい", + "no": "いいえ", + "update": "更新", + "remove": "削除する", + "search": "サーチ", + "clear-search": "検索をクリアする", + "assign": "割り当てます", + "unassign": "割り当て解除", + "share": "シェア", + "make-private": "プライベートにする", + "apply": "適用", + "apply-changes": "変更を適用する", + "edit-mode": "編集モード", + "enter-edit-mode": "編集モードに入る", + "decline-changes": "変更を拒否する", + "close": "閉じる", + "back": "バック", + "run": "走る", + "sign-in": "サインイン!", + "edit": "編集", + "view": "ビュー", + "create": "作成する", + "drag": "ドラッグ", + "refresh": "リフレッシュ", + "undo": "元に戻す", + "copy": "コピー", + "paste": "ペースト", + "copy-reference": "コピーリファレンス", + "paste-reference": "参照貼り付け", + "import": "インポート", + "export": "輸出する", + "share-via": "{{provider}}" + }, + "aggregation": { + "aggregation": "集約", + "function": "データ集約機能", + "limit": "最大値", + "group-interval": "グループ化の間隔", + "min": "分", + "max": "最大", + "avg": "平均", + "sum": "和", + "count": "カウント", + "none": "なし" + }, + "admin": { + "general": "一般", + "general-settings": "一般設定", + "outgoing-mail": "送信メール", + "outgoing-mail-settings": "送信メールの設定", + "system-settings": "システム設定", + "test-mail-sent": "テストメールが正常に送信されました!", + "base-url": "ベースURL", + "base-url-required": "ベースURLは必須です。", + "mail-from": "メール", + "mail-from-required": "メールの送信元が必要です。", + "smtp-protocol": "SMTPプロトコル", + "smtp-host": "SMTPホスト", + "smtp-host-required": "SMTPホストが必要です。", + "smtp-port": "SMTPポート", + "smtp-port-required": "smtpポートを指定する必要があります。", + "smtp-port-invalid": "それは有効なsmtpポートのようには見えません。", + "timeout-msec": "タイムアウト(ミリ秒)", + "timeout-required": "タイムアウトが必要です。", + "timeout-invalid": "それは有効なタイムアウトのようには見えません。", + "enable-tls": "TLSを有効にする", + "tls-version": "TLSバージョン", + "enter-tls-version" : "TLSバージョンを入力してください", + "send-test-mail": "テストメールを送信する" + }, + "alarm": { + "alarm": "警報", + "alarms": "アラーム", + "select-alarm": "アラームを選択", + "no-alarms-matching": "'{{entity}}'発見されました。", + "alarm-required": "アラームが必要です", + "alarm-status": "アラーム状態", + "search-status": { + "ANY": "どれか", + "ACTIVE": "アクティブ", + "CLEARED": "クリアされた", + "ACK": "承認された", + "UNACK": "未確認の" + }, + "display-status": { + "ACTIVE_UNACK": "アクティブ未確認", + "ACTIVE_ACK": "Active Acknowledged", + "CLEARED_UNACK": "クリアされた未確認のメッセージ", + "CLEARED_ACK": "承認された承認済み" + }, + "no-alarms-prompt": "アラームが見つかりません", + "created-time": "作成時刻", + "type": "タイプ", + "severity": "重大度", + "originator": "創始者", + "originator-type": "発信者タイプ", + "details": "詳細", + "status": "状態", + "alarm-details": "アラームの詳細", + "start-time": "始まる時間", + "end-time": "終了時間", + "ack-time": "確認された時間", + "clear-time": "クリアされた時間", + "severity-critical": "クリティカル", + "severity-major": "メジャー", + "severity-minor": "マイナー", + "severity-warning": "警告", + "severity-indeterminate": "不確定", + "acknowledge": "認める", + "clear": "クリア", + "search": "アラームの検索", + "selected-alarms": "{ count, plural, 1 {1 alarm} other {# alarms} }選択された", + "no-data": "表示するデータがありません", + "polling-interval": "アラームポーリング間隔(秒)", + "polling-interval-required": "アラームのポーリング間隔が必要です。", + "min-polling-interval-message": "少なくとも1秒間のポーリング間隔が許可されます。", + "aknowledge-alarms-title": "{ count, plural, 1 {1 alarm} other {# alarms} }", + "aknowledge-alarms-text": "{ count, plural, 1 {1 alarm} other {# alarms} }?", + "clear-alarms-title": "{ count, plural, 1 {1 alarm} other {# alarms} }", + "clear-alarms-text": "{ count, plural, 1 {1 alarm} other {# alarms} }?" + }, + "alias": { + "add": "エイリアスを追加する", + "edit": "エイリアスを編集する", + "name": "エイリアス名", + "name-required": "エイリアス名は必須です", + "duplicate-alias": "同じ名前のエイリアスは既に存在します。", + "filter-type-single-entity": "単一のエンティティ", + "filter-type-entity-list": "エンティティリスト", + "filter-type-entity-name": "エンティティ名", + "filter-type-state-entity": "ダッシュボード状態からのエンティティ", + "filter-type-state-entity-description": "ダッシュボードの状態パラメータから取得されたエンティティ", + "filter-type-asset-type": "資産の種類", + "filter-type-asset-type-description": "'{{assetType}}'", + "filter-type-asset-type-and-name-description": "'{{assetType}}''{{prefix}}'", + "filter-type-device-type": "デバイスタイプ", + "filter-type-device-type-description": "'{{deviceType}}'", + "filter-type-device-type-and-name-description": "'{{deviceType}}''{{prefix}}'", + "filter-type-relations-query": "関係クエリ", + "filter-type-relations-query-description": "{{entities}}{{relationType}}{{direction}}{{rootEntity}}", + "filter-type-asset-search-query": "資産検索クエリ", + "filter-type-asset-search-query-description": "{{assetTypes}}{{relationType}}{{direction}}{{rootEntity}}", + "filter-type-device-search-query": "デバイス検索クエリ", + "filter-type-device-search-query-description": "{{deviceTypes}}{{relationType}}{{direction}}{{rootEntity}}", + "entity-filter": "エンティティフィルタ", + "resolve-multiple": "複数のエンティティとして解決する", + "filter-type": "フィルタタイプ", + "filter-type-required": "フィルタタイプが必要です。", + "entity-filter-no-entity-matched": "指定されたフィルタに一致するエンティティは見つかりませんでした。", + "no-entity-filter-specified": "エンティティフィルタが指定されていない", + "root-state-entity": "ルートとしてダッシュボードの状態エンティティを使用する", + "root-entity": "ルートエンティティ", + "state-entity-parameter-name": "状態エンティティのパラメータ名", + "default-state-entity": "デフォルト状態エンティティ", + "default-entity-parameter-name": "デフォルトでは", + "max-relation-level": "最大関連レベル", + "unlimited-level": "無制限レベル", + "state-entity": "ダッシュボードの状態エンティティ", + "all-entities": "すべてのエンティティ", + "any-relation": "どれか" + }, + "asset": { + "asset": "資産", + "assets": "資産", + "management": "資産運用管理", + "view-assets": "アセットの表示", + "add": "アセットを追加", + "assign-to-customer": "顧客に割り当てる", + "assign-asset-to-customer": "顧客に資産を割り当てる", + "assign-asset-to-customer-text": "顧客に割り当てる資産を選択してください", + "no-assets-text": "アセットが見つかりません", + "assign-to-customer-text": "資産を割り当てる顧客を選択してください", + "public": "パブリック", + "assignedToCustomer": "顧客に割り当てられた", + "make-public": "アセットを公開する", + "make-private": "アセットをプライベートにする", + "unassign-from-customer": "顧客からの割り当て解除", + "delete": "アセットを削除", + "asset-public": "資産は公開されています", + "asset-type": "資産の種類", + "asset-type-required": "資産の種類が必要です。", + "select-asset-type": "アセットタイプを選択", + "enter-asset-type": "アセットタイプを入力", + "any-asset": "すべてのアセット", + "no-asset-types-matching": "'{{entitySubtype}}'発見されました。", + "asset-type-list-empty": "選択されたアセットタイプはありません。", + "asset-types": "資産タイプ", + "name": "名", + "name-required": "名前は必須です。", + "description": "説明", + "type": "タイプ", + "type-required": "タイプが必要です。", + "details": "詳細", + "events": "イベント", + "add-asset-text": "新しいアセットを追加する", + "asset-details": "資産の詳細", + "assign-assets": "アセットの割り当て", + "assign-assets-text": "{ count, plural, 1 {1 asset} other {# assets} }顧客に", + "delete-assets": "アセットを削除する", + "unassign-assets": "アセットの割り当てを解除する", + "unassign-assets-action-title": "{ count, plural, 1 {1 asset} other {# assets} }顧客から", + "assign-new-asset": "新しいアセットを割り当てる", + "delete-asset-title": "'{{assetName}}'?", + "delete-asset-text": "確認後、資産と関連するすべてのデータが回復不能になることに注意してください。", + "delete-assets-title": "{ count, plural, 1 {1 asset} other {# assets} }?", + "delete-assets-action-title": "{ count, plural, 1 {1 asset} other {# assets} }", + "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 asset} other {# assets} }?", + "unassign-assets-text": "確認後、選択されたすべての資産が割り当て解除され、顧客がアクセスできなくなります。", + "copyId": "アセットIDをコピーする", + "idCopiedMessage": "アセットIDがクリップボードにコピーされました", + "select-asset": "アセットを選択", + "no-assets-matching": "'{{entity}}'発見されました。", + "asset-required": "資産が必要です", + "name-starts-with": "アセット名はで始まります", + "label": "ラベル" + }, + "attribute": { + "attributes": "属性", + "latest-telemetry": "最新テレメトリ", + "attributes-scope": "エンティティ属性のスコープ", + "scope-latest-telemetry": "最新テレメトリ", + "scope-client": "クライアントの属性", + "scope-server": "サーバーの属性", + "scope-shared": "共有属性", + "add": "属性を追加する", + "key": "キー", + "last-update-time": "最終更新時間", + "key-required": "属性キーは必須です。", + "value": "値", + "value-required": "属性値は必須です。", + "delete-attributes-title": "{ count, plural, 1 {1 attribute} other {# attributes} }?", + "delete-attributes-text": "注意してください。確認後、選択したすべての属性が削除されます。", + "delete-attributes": "属性を削除する", + "enter-attribute-value": "属性値を入力", + "show-on-widget": "ウィジェットで表示", + "widget-mode": "ウィジェットモード", + "next-widget": "次のウィジェット", + "prev-widget": "前のウィジェット", + "add-to-dashboard": "ダッシュボードに追加", + "add-widget-to-dashboard": "ウィジェットをダッシュ​​ボードに追加する", + "selected-attributes": "{ count, plural, 1 {1 attribute} other {# attributes} }選択された", + "selected-telemetry": "{ count, plural, 1 {1 telemetry unit} other {# telemetry units} }選択された" + }, + "audit-log": { + "audit": "監査", + "audit-logs": "監査ログ", + "timestamp": "タイムスタンプ", + "entity-type": "エンティティタイプ", + "entity-name": "エンティティ名", + "user": "ユーザー", + "type": "タイプ", + "status": "状態", + "details": "詳細", + "type-added": "追加された", + "type-deleted": "削除済み", + "type-updated": "更新しました", + "type-attributes-updated": "属性が更新されました", + "type-attributes-deleted": "属性が削除されました", + "type-rpc-call": "RPC呼び出し", + "type-credentials-updated": "資格が更新されました", + "type-assigned-to-customer": "顧客に割り当てられた", + "type-unassigned-from-customer": "顧客から割り当てられていない", + "type-activated": "活性化", + "type-suspended": "一時停止中", + "type-credentials-read": "信用証明書を読む", + "type-attributes-read": "読み取られた属性", + "type-relation-add-or-update": "関係が更新されました", + "type-relation-delete": "関係が削除されました", + "type-relations-delete": "すべてのリレーションを削除", + "type-alarm-ack": "承認された", + "type-alarm-clear": "クリアされた", + "status-success": "成功", + "status-failure": "失敗", + "audit-log-details": "監査ログの詳細", + "no-audit-logs-prompt": "ログが見つかりません", + "action-data": "行動データ", + "failure-details": "失敗の詳細", + "search": "監査ログの検索", + "clear-search": "検索をクリアする" + }, + "confirm-on-exit": { + "message": "保存されていない変更があります。あなたは本当にこのページを出るのですか?", + "html-message": "保存していない変更があります。
このページを終了してもよろしいですか?", + "title": "保存されていない変更" + }, + "contact": { + "country": "国", + "city": "シティ", + "state": "州/県", + "postal-code": "郵便番号", + "postal-code-invalid": "無効な郵便番号形式です。", + "address": "住所", + "address2": "アドレス2", + "phone": "電話", + "email": "Eメール", + "no-address": "住所がありません" + }, + "common": { + "username": "ユーザー名", + "password": "パスワード", + "enter-username": "ユーザーネームを入力してください", + "enter-password": "パスワードを入力する", + "enter-search": "検索を入力" + }, + "content-type": { + "json": "Json", + "text": "テキスト", + "binary": "バイナリ(Base64)" + }, + "customer": { + "customer": "顧客", + "customers": "顧客", + "management": "顧客管理", + "dashboard": "カスタマーダッシュボード", + "dashboards": "カスタマーダッシュボード", + "devices": "顧客デバイス", + "assets": "顧客資産", + "public-dashboards": "パブリックダッシュボード", + "public-devices": "パブリックデバイス", + "public-assets": "公的資産", + "add": "顧客を追加", + "delete": "顧客を削除する", + "manage-customer-users": "顧客ユーザーを管理する", + "manage-customer-devices": "顧客のデバイスを管理する", + "manage-customer-dashboards": "顧客ダッシュボードの管理", + "manage-public-devices": "パブリックデバイスを管理する", + "manage-public-dashboards": "公開ダッシュボードの管理", + "manage-customer-assets": "顧客資産の管理", + "manage-public-assets": "公的資産を管理する", + "add-customer-text": "新規顧客を追加", + "no-customers-text": "顧客が見つかりません", + "customer-details": "お客様情報", + "delete-customer-title": "'{{customerTitle}}'?", + "delete-customer-text": "確認後、お客様および関連するすべてのデータが回復不能になるので注意してください。", + "delete-customers-title": "{ count, plural, 1 {1 customer} other {# customers} }?", + "delete-customers-action-title": "{ count, plural, 1 {1 customer} other {# customers} }", + "delete-customers-text": "確認後、選択したすべての顧客は削除され、関連するすべてのデータは回復不能になります。", + "manage-users": "ユーザーを管理する", + "manage-assets": "アセットを管理する", + "manage-devices": "デバイスを管理する", + "manage-dashboards": "ダッシュボードの管理", + "title": "タイトル", + "title-required": "タイトルは必須です。", + "description": "説明", + "details": "詳細", + "events": "イベント", + "copyId": "顧客IDをコピー", + "idCopiedMessage": "顧客IDがクリップボードにコピーされました", + "select-customer": "顧客を選択", + "no-customers-matching": "'{{entity}}'発見されました。", + "customer-required": "顧客は必須です", + "select-default-customer": "デフォルトの顧客を選択", + "default-customer": "デフォルトの顧客", + "default-customer-required": "テナントレベルのダッシュボードをデバッグするには、デフォルトの顧客が必要です" + }, + "datetime": { + "date-from": "デートから", + "time-from": "からの時間", + "date-to": "日付", + "time-to": "の時間" + }, + "dashboard": { + "dashboard": "ダッシュボード", + "dashboards": "ダッシュボード", + "management": "ダッシュボード管理", + "view-dashboards": "ダッシュボードを表示する", + "add": "ダッシュボードを追加", + "assign-dashboard-to-customer": "顧客にダッシュボードを割り当てる", + "assign-dashboard-to-customer-text": "顧客に割り当てるダッシュボードを選択してください", + "assign-to-customer-text": "ダッシュボードを割り当てる顧客を選択してください", + "assign-to-customer": "顧客に割り当てる", + "unassign-from-customer": "顧客からの割り当て解除", + "make-public": "ダッシュボードを公開する", + "make-private": "ダッシュボードを非公開にする", + "manage-assigned-customers": "割り当てられた顧客を管理する", + "assigned-customers": "割り当てられた顧客", + "assign-to-customers": "顧客にダッシュボードを割り当てる", + "assign-to-customers-text": "ダッシュボードを割り当てる顧客を選択してください", + "unassign-from-customers": "顧客からのダッシュボードの割り当て解除", + "unassign-from-customers-text": "ダッシュボードから割り当て解除する顧客を選択してください", + "no-dashboards-text": "ダッシュボードが見つかりません", + "no-widgets": "ウィジェットは設定されていません", + "add-widget": "新しいウィジェットを追加", + "title": "タイトル", + "select-widget-title": "ウィジェットを選択", + "select-widget-subtitle": "利用可能なウィジェットタイプのリスト", + "delete": "ダッシュボードの削除", + "title-required": "タイトルは必須です。", + "description": "説明", + "details": "詳細", + "dashboard-details": "ダッシュボードの詳細", + "add-dashboard-text": "新しいダッシュボードを追加する", + "assign-dashboards": "ダッシュボードの割り当て", + "assign-new-dashboard": "新しいダッシュボードを割り当てる", + "assign-dashboards-text": "{ count, plural, 1 {1 dashboard} other {# dashboards} }顧客に", + "unassign-dashboards-action-text": "{ count, plural, 1 {1 dashboard} other {# dashboards} }顧客から", + "delete-dashboards": "ダッシュボードの削除", + "unassign-dashboards": "ダッシュボードの割り当てを解除する", + "unassign-dashboards-action-title": "{ count, plural, 1 {1 dashboard} other {# dashboards} }顧客から", + "delete-dashboard-title": "'{{dashboardTitle}}'?", + "delete-dashboard-text": "確認後、ダッシュボードとすべての関連データが回復不能になるので注意してください。", + "delete-dashboards-title": "{ count, plural, 1 {1 dashboard} other {# dashboards} }?", + "delete-dashboards-action-title": "{ count, plural, 1 {1 dashboard} other {# dashboards} }", + "delete-dashboards-text": "注意してください。確認後、選択したダッシュボードはすべて削除され、関連するすべてのデータは回復不能になります。", + "unassign-dashboard-title": "'{{dashboardTitle}}'?", + "unassign-dashboard-text": "確認後、ダッシュボードは割り当てられなくなり、顧客はアクセスできなくなります。", + "unassign-dashboard": "ダッシュボードの割り当てを解除する", + "unassign-dashboards-title": "{ count, plural, 1 {1 dashboard} other {# dashboards} }?", + "unassign-dashboards-text": "確認の後、選択したすべてのダッシュボードは割り当てられなくなり、顧客はアクセスできなくなります。", + "public-dashboard-title": "ダッシュボードは公開されました", + "public-dashboard-text": "{{dashboardTitle}} is now public and accessible via next public link:", + "public-dashboard-notice": "注:データにアクセスするために、関連するデバイスを公開することを忘れないでください。", + "make-private-dashboard-title": "'{{dashboardTitle}}'プライベート?", + "make-private-dashboard-text": "確認の後、ダッシュボードはプライベートにされ、他の人がアクセスすることはできません。", + "make-private-dashboard": "ダッシュボードを非公開にする", + "socialshare-text": "'{{dashboardTitle}}'ThingsBoardを搭載", + "socialshare-title": "'{{dashboardTitle}}'ThingsBoardを搭載", + "select-dashboard": "ダッシュボードを選択", + "no-dashboards-matching": "'{{entity}}'発見されました。", + "dashboard-required": "ダッシュボードが必要です。", + "select-existing": "既存のダッシュボードを選択", + "create-new": "新しいダッシュボードを作成する", + "new-dashboard-title": "新しいダッシュボードのタイトル", + "open-dashboard": "ダッシュボードを開く", + "set-background": "背景を設定する", + "background-color": "背景色", + "background-image": "背景画像", + "background-size-mode": "背景サイズモード", + "no-image": "選択した画像がありません", + "drop-image": "画像をドロップするか、クリックしてアップロードするファイルを選択します。", + "settings": "設定", + "columns-count": "列数", + "columns-count-required": "列数が必要です。", + "min-columns-count-message": "わずか10の最小列数が許可されます。", + "max-columns-count-message": "最大1000の列カウントのみが許可されます。", + "widgets-margins": "ウィジェット間のマージン", + "horizontal-margin": "水平マージン", + "horizontal-margin-required": "水平余白値が必要です。", + "min-horizontal-margin-message": "最小水平マージン値としては0だけが許容されます。", + "max-horizontal-margin-message": "最大水平マージン値は50だけです。", + "vertical-margin": "垂直マージン", + "vertical-margin-required": "垂直マージン値が必要です。", + "min-vertical-margin-message": "最小の垂直マージン値として0のみが許可されます。", + "max-vertical-margin-message": "最大垂直マージン値は50のみです。", + "autofill-height": "自動レイアウトの高さ", + "mobile-layout": "モバイルレイアウトの設定", + "mobile-row-height": "モバイル行の高さ、px", + "mobile-row-height-required": "モバイル行の高さ値が必要です。", + "min-mobile-row-height-message": "最小の行の高さの値として、5ピクセルしか許可されません。", + "max-mobile-row-height-message": "移動可能な行の高さの最大値として許可されるのは200ピクセルだけです。", + "display-title": "ダッシュボードのタイトルを表示する", + "toolbar-always-open": "ツールバーを開いたままにする", + "title-color": "タイトルカラー", + "display-dashboards-selection": "ダッシュボードの選択を表示する", + "display-entities-selection": "エンティティの選択を表示する", + "display-dashboard-timewindow": "タイムウィンドウを表示する", + "display-dashboard-export": "エクスポートの表示", + "import": "インポートダッシュボード", + "export": "エクスポートダッシュボード", + "export-failed-error": "{{error}}", + "create-new-dashboard": "新しいダッシュボードを作成する", + "dashboard-file": "ダッシュボードファイル", + "invalid-dashboard-file-error": "ダッシュボードをインポートできません:ダッシュボードのデータ構造が無効です。", + "dashboard-import-missing-aliases-title": "インポートされたダッシュボードで使用されるエイリアスを設定する", + "create-new-widget": "新しいウィジェットを作成する", + "import-widget": "インポートウィジェット", + "widget-file": "ウィジェットファイル", + "invalid-widget-file-error": "ウィジェットをインポートできません:ウィジェットのデータ構造が無効です。", + "widget-import-missing-aliases-title": "インポートされたウィジェットで使用されるエイリアスを設定する", + "open-toolbar": "ダッシュボードツールバーを開く", + "close-toolbar": "ツールバーを閉じる", + "configuration-error": "設定エラー", + "alias-resolution-error-title": "ダッシュボードエイリアス設定エラー", + "invalid-aliases-config": "エイリアスフィルタの一部に一致するデバイスを見つけることができません。
この問題を解決するには、管理者に連絡してください。", + "select-devices": "デバイスの選択", + "assignedToCustomer": "顧客に割り当てられた", + "assignedToCustomers": "顧客に割り当てられた", + "public": "パブリック", + "public-link": "パブリックリンク", + "copy-public-link": "パブリックリンクをコピーする", + "public-link-copied-message": "ダッシュボードのパブリックリンクがクリップボードにコピーされました", + "manage-states": "ダッシュボードの状態を管理する", + "states": "ダッシュボードの状態", + "search-states": "検索ダッシュボードの状態", + "selected-states": "{ count, plural, 1 {1 dashboard state} other {# dashboard states} }選択された", + "edit-state": "ダッシュボードの状態を編集する", + "delete-state": "ダッシュボードの状態を削除する", + "add-state": "ダッシュボードの状態を追加する", + "state": "ダッシュボードの状態", + "state-name": "名", + "state-name-required": "ダッシュボードの状態名は必須です。", + "state-id": "状態ID", + "state-id-required": "ダッシュボードの状態IDは必須です。", + "state-id-exists": "同じIDを持つダッシュボードの状態は既に存在します。", + "is-root-state": "ルート状態", + "delete-state-title": "ダッシュボードの状態を削除する", + "delete-state-text": "'{{stateName}}'?", + "show-details": "詳細を表示", + "hide-details": "詳細を隠す", + "select-state": "ターゲット状態を選択する", + "state-controller": "状態コントローラ" + }, + "datakey": { + "settings": "設定", + "advanced": "上級", + "label": "ラベル", + "color": "色", + "units": "値の隣に表示する特別なシンボル", + "decimals": "浮動小数点の後の桁数", + "data-generation-func": "データ生成関数", + "use-data-post-processing-func": "データ後処理機能を使用する", + "configuration": "データキー設定", + "timeseries": "タイムズ", + "attributes": "属性", + "alarm": "アラームフィールド", + "timeseries-required": "エンティティの時系列データが必要です。", + "timeseries-or-attributes-required": "エンティティのtimeseries /属性は必須です。", + "maximum-timeseries-or-attributes": "{ count, plural, 1 {1 timeseries/attribute is allowed.} other {# timeseries/attributes are allowed} }", + "alarm-fields-required": "アラームフィールドが必要です。", + "function-types": "関数型", + "function-types-required": "関数型が必要です。", + "maximum-function-types": "{ count, plural, 1 {1 function type is allowed.} other {# function types are allowed} }" + }, + "datasource": { + "type": "データソースタイプ", + "name": "名", + "add-datasource-prompt": "データソースを追加してください" + }, + "details": { + "edit-mode": "編集モード", + "toggle-edit-mode": "編集モードを切り替える" + }, + "device": { + "device": "デバイス", + "device-required": "デバイスが必要です。", + "devices": "デバイス", + "management": "端末管理", + "view-devices": "デバイスの表示", + "device-alias": "デバイスエイリアス", + "aliases": "デバイスエイリアス", + "no-alias-matching": "'{{alias}}'見つかりません。", + "no-aliases-found": "別名は見つかりませんでした。", + "no-key-matching": "'{{key}}'見つかりません。", + "no-keys-found": "キーが見つかりません。", + "create-new-alias": "新しいものを作成してください!", + "create-new-key": "新しいものを作成してください!", + "duplicate-alias-error": "'{{alias}}'
デバイスエイリアスは、ダッシュボード内で一意である必要があります。", + "configure-alias": "'{{alias}}'エイリアス", + "no-devices-matching": "'{{entity}}'発見されました。", + "alias": "エイリアス", + "alias-required": "デバイスエイリアスが必要です。", + "remove-alias": "デバイスエイリアスを削除する", + "add-alias": "デバイスエイリアスを追加する", + "name-starts-with": "デバイス名はで始まります", + "device-list": "デバイスリスト", + "use-device-name-filter": "フィルタを使用する", + "device-list-empty": "デバイスが選択されていません。", + "device-name-filter-required": "デバイス名フィルタが必要です。", + "device-name-filter-no-device-matched": "'{{device}}'発見されました。", + "add": "デバイスを追加", + "assign-to-customer": "顧客に割り当てる", + "assign-device-to-customer": "顧客にデバイスを割り当てる", + "assign-device-to-customer-text": "顧客に割り当てるデバイスを選択してください", + "make-public": "端末を公開する", + "make-private": "デバイスを非公開にする", + "no-devices-text": "デバイスが見つかりません", + "assign-to-customer-text": "デバイスを割り当てる顧客を選択してください", + "device-details": "デバイスの詳細", + "add-device-text": "新しいデバイスを追加する", + "credentials": "資格情報", + "manage-credentials": "資格情報を管理する", + "delete": "デバイスを削除する", + "assign-devices": "デバイスを割り当てる", + "assign-devices-text": "{ count, plural, 1 {1 device} other {# devices} }顧客に", + "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": "確認後、デバイスとそのすべてのデータは非公開になり、他人がアクセスできなくなります。", + "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": "デバイス資格情報", + "credentials-type": "資格情報タイプ", + "access-token": "アクセストークン", + "access-token-required": "アクセストークンが必要です。", + "access-token-invalid": "アクセストークンの長さは、1〜20文字でなければなりません。", + "rsa-key": "RSA公開鍵", + "rsa-key-required": "RSA公開鍵が必要です。", + "secret": "秘密", + "secret-required": "秘密が必要です。", + "device-type": "デバイスタイプ", + "device-type-required": "デバイスタイプが必要です。", + "select-device-type": "デバイスタイプを選択", + "enter-device-type": "デバイスタイプを入力", + "any-device": "すべてのデバイス", + "no-device-types-matching": "'{{entitySubtype}}'発見されました。", + "device-type-list-empty": "選択されたデバイスタイプはありません。", + "device-types": "デバイスの種類", + "name": "名", + "name-required": "名前は必須です。", + "description": "説明", + "events": "イベント", + "details": "詳細", + "copyId": "デバイスIDをコピーする", + "copyAccessToken": "コピーアクセストークン", + "idCopiedMessage": "デバイスIDがクリップボードにコピーされました", + "accessTokenCopiedMessage": "デバイスアクセストークンがクリップボードにコピーされました", + "assignedToCustomer": "顧客に割り当てられた", + "unable-delete-device-alias-title": "デバイスエイリアスを削除できません", + "unable-delete-device-alias-text": "'{{deviceAlias}}'{{widgetsList}}", + "is-gateway": "ゲートウェイです", + "public": "パブリック", + "device-public": "デバイスは公開されています", + "select-device": "デバイスの選択" + }, + "dialog": { + "close": "ダイアログを閉じる" + }, + "error": { + "unable-to-connect": "サーバーに接続できません!インターネット接続を確認してください。", + "unhandled-error-code": "{{errorCode}}", + "unknown-error": "不明なエラー" + }, + "entity": { + "entity": "エンティティ", + "entities": "エンティティ", + "aliases": "エンティティエイリアス", + "entity-alias": "エンティティエイリアス", + "unable-delete-entity-alias-title": "エンティティエイリアスを削除できません", + "unable-delete-entity-alias-text": "'{{entityAlias}}'{{widgetsList}}", + "duplicate-alias-error": "'{{alias}}'
エンティティのエイリアスは、ダッシュボード内で一意である必要があります。", + "missing-entity-filter-error": "'{{alias}}'.", + "configure-alias": "'{{alias}}'エイリアス", + "alias": "エイリアス", + "alias-required": "エンティティエイリアスが必要です。", + "remove-alias": "エンティティエイリアスを削除する", + "add-alias": "エンティティエイリアスを追加する", + "entity-list": "エンティティリスト", + "entity-type": "エンティティタイプ", + "entity-types": "エンティティタイプ", + "entity-type-list": "エンティティタイプリスト", + "any-entity": "任意のエンティティ", + "enter-entity-type": "エンティティタイプを入力", + "no-entities-matching": "'{{entity}}'発見されました。", + "no-entity-types-matching": "'{{entityType}}'発見されました。", + "name-starts-with": "名前はで始まる", + "use-entity-name-filter": "フィルタを使用する", + "entity-list-empty": "選択されたエンティティはありません", + "entity-type-list-empty": "エンティティタイプは選択されていません。", + "entity-name-filter-required": "エンティティ名フィルタが必要です。", + "entity-name-filter-no-entity-matched": "'{{entity}}'発見されました。", + "all-subtypes": "すべて", + "select-entities": "エンティティの選択", + "no-aliases-found": "別名は見つかりませんでした。", + "no-alias-matching": "'{{alias}}'見つかりません。", + "create-new-alias": "新しいものを作成してください!", + "key": "キー", + "key-name": "キー名", + "no-keys-found": "キーが見つかりません。", + "no-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": "資産", + "type-assets": "資産", + "list-of-assets": "{ count, plural, 1 {One asset} other {List of # assets} }", + "asset-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}}'", + "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-customer": "顧客", + "type-customers": "顧客", + "list-of-customers": "{ count, plural, 1 {One customer} other {List of # customers} }", + "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": "ダッシュボード", + "type-dashboards": "ダッシュボード", + "list-of-dashboards": "{ count, plural, 1 {One dashboard} other {List of # dashboards} }", + "dashboard-name-starts-with": "'{{prefix}}'", + "type-alarm": "警報", + "type-alarms": "アラーム", + "list-of-alarms": "{ count, plural, 1 {One alarms} other {List of # alarms} }", + "alarm-name-starts-with": "'{{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-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} }選択された", + "entity-name": "エンティティ名", + "details": "エンティティの詳細", + "no-entities-prompt": "エンティティが見つかりません", + "no-data": "表示するデータがありません" + }, + "event": { + "event-type": "イベントタイプ", + "type-error": "エラー", + "type-lc-event": "ライフサイクルイベント", + "type-stats": "統計", + "type-debug-rule-node": "デバッグ", + "type-debug-rule-chain": "デバッグ", + "no-events-prompt": "イベントは見つかりませんでした", + "error": "エラー", + "alarm": "警報", + "event-time": "イベント時間", + "server": "サーバ", + "body": "体", + "method": "方法", + "type": "タイプ", + "entity": "エンティティ", + "message-id": "メッセージID", + "message-type": "メッセージタイプ", + "data-type": "データ・タイプ", + "relation-type": "関係タイプ", + "metadata": "メタデータ", + "data": "データ", + "event": "イベント", + "status": "状態", + "success": "成功", + "failed": "失敗", + "messages-processed": "処理されたメッセージ", + "errors-occurred": "エラーが発生しました" + }, + "extension": { + "extensions": "拡張機能", + "selected-extensions": "{ count, plural, 1 {1 extension} other {# extensions} }選択された", + "type": "タイプ", + "key": "キー", + "value": "値", + "id": "イド", + "extension-id": "内線番号", + "extension-type": "拡張タイプ", + "transformer-json": "JSON *", + "unique-id-required": "現在の拡張IDは既に存在します。", + "delete": "拡張子を削除", + "add": "内線番号を追加", + "edit": "拡張機能を編集する", + "delete-extension-title": "'{{extensionId}}'?", + "delete-extension-text": "確認後、拡張子と関連するすべてのデータが回復不能になることに注意してください。", + "delete-extensions-title": "{ count, plural, 1 {1 extension} other {# extensions} }?", + "delete-extensions-text": "注意してください。確認後、選択したすべての内線番号が削除されます。", + "converters": "コンバーター", + "converter-id": "コンバーターID", + "configuration": "構成", + "converter-configurations": "コンバータ構成", + "token": "セキュリティトークン", + "add-converter": "コンバータを追加する", + "add-config": "コンバータ設定を追加する", + "device-name-expression": "デバイス名式", + "device-type-expression": "デバイスタイプの式", + "custom": "カスタム", + "to-double": "ダブル", + "transformer": "トランス", + "json-required": "トランスフォーマーjsonが必要です。", + "json-parse": "変圧器jsonを解析できません。", + "attributes": "属性", + "add-attribute": "属性を追加する", + "add-map": "マッピング要素を追加する", + "timeseries": "タイムズ", + "add-timeseries": "時系列を追加する", + "field-required": "フィールドは必須項目です", + "brokers": "ブローカー", + "add-broker": "ブローカーを追加", + "host": "ホスト", + "port": "ポート", + "port-range": "ポートは1〜65535の範囲内にある必要があります。", + "ssl": "SSL", + "credentials": "資格情報", + "username": "ユーザー名", + "password": "パスワード", + "retry-interval": "ミリ秒単位の再試行間隔", + "anonymous": "匿名", + "basic": "ベーシック", + "pem": "PEM", + "ca-cert": "CA証明書ファイル*", + "private-key": "秘密鍵ファイル*", + "cert": "証明書ファイル*", + "no-file": "ファイルが選択されていません。", + "drop-file": "ファイルをドロップするか、クリックしてアップロードするファイルを選択します。", + "mapping": "マッピング", + "topic-filter": "トピックフィルタ", + "converter-type": "コンバータタイプ", + "converter-json": "Json", + "json-name-expression": "デバイス名json式", + "topic-name-expression": "デバイス名トピック表現", + "json-type-expression": "デバイスタイプjson式", + "topic-type-expression": "デバイスタイプトピック表現", + "attribute-key-expression": "属性キー式", + "attr-json-key-expression": "属性キーjson式", + "attr-topic-key-expression": "属性キートピック式", + "request-id-expression": "要求ID式", + "request-id-json-expression": "リクエストID json式", + "request-id-topic-expression": "リクエストIDトピック表現", + "response-topic-expression": "応答トピック表現", + "value-expression": "値式", + "topic": "トピック", + "timeout": "タイムアウト(ミリ秒)", + "converter-json-required": "コンバータjsonが必要です。", + "converter-json-parse": "コンバータjsonを解析できません。", + "filter-expression": "フィルタ式", + "connect-requests": "接続要求", + "add-connect-request": "接続要求を追加", + "disconnect-requests": "切断要求", + "add-disconnect-request": "切断リクエストを追加する", + "attribute-requests": "属性要求", + "add-attribute-request": "属性要求を追加する", + "attribute-updates": "属性の更新", + "add-attribute-update": "属性の更新を追加する", + "server-side-rpc": "サーバー側RPC", + "add-server-side-rpc-request": "サーバー側RPC要求を追加する", + "device-name-filter": "デバイス名フィルタ", + "attribute-filter": "属性フィルタ", + "method-filter": "方法フィルター", + "request-topic-expression": "トピック表現を要求する", + "response-timeout": "応答タイムアウト(ミリ秒)", + "topic-expression": "トピック表現", + "client-scope": "クライアントスコープ", + "add-device": "デバイスを追加", + "opc-server": "サーバー", + "opc-add-server": "サーバーを追加", + "opc-add-server-prompt": "サーバーを追加してください", + "opc-application-name": "アプリケーション名", + "opc-application-uri": "アプリケーションURI", + "opc-scan-period-in-seconds": "スキャン時間(秒)", + "opc-security": "セキュリティ", + "opc-identity": "身元", + "opc-keystore": "キーストア", + "opc-type": "タイプ", + "opc-keystore-type": "タイプ", + "opc-keystore-location": "ロケーション*", + "opc-keystore-password": "パスワード", + "opc-keystore-alias": "エイリアス", + "opc-keystore-key-password": "キーのパスワード", + "opc-device-node-pattern": "デバイスノードパターン", + "opc-device-name-pattern": "デバイス名パターン", + "modbus-server": "サーバー/スレーブ", + "modbus-add-server": "サーバー/スレーブを追加する", + "modbus-add-server-prompt": "サーバー/スレーブを追加してください", + "modbus-transport": "輸送", + "modbus-port-name": "シリアルポート名", + "modbus-encoding": "エンコーディング", + "modbus-parity": "パリティ", + "modbus-baudrate": "ボーレート", + "modbus-databits": "データビット", + "modbus-stopbits": "ストップビット", + "modbus-databits-range": "データビットは7〜8の範囲内にある必要があります。", + "modbus-stopbits-range": "ストップビットは1〜2の範囲内でなければなりません。", + "modbus-unit-id": "ユニットID", + "modbus-unit-id-range": "ユニットIDは1〜247の範囲で指定してください。", + "modbus-device-name": "装置名", + "modbus-poll-period": "投票期間(ミリ秒)", + "modbus-attributes-poll-period": "属性のポーリング期間(ミリ秒)", + "modbus-timeseries-poll-period": "時系列ポーリング期間(ミリ秒)", + "modbus-poll-period-range": "投票期間は正の値でなければなりません。", + "modbus-tag": "タグ", + "modbus-function": "関数", + "modbus-register-address": "登録アドレス", + "modbus-register-address-range": "レジスタのアドレスは0〜65535の範囲内である必要があります。", + "modbus-register-bit-index": "ビットインデックス", + "modbus-register-bit-index-range": "ビットインデックスは0〜15の範囲内である必要があります。", + "modbus-register-count": "レジスタ数", + "modbus-register-count-range": "レジスタ数は正の値でなければなりません。", + "modbus-byte-order": "バイト順", + "sync": { + "status": "状態", + "sync": "同期", + "not-sync": "同期しない", + "last-sync-time": "前回の同期時間", + "not-available": "利用不可" + }, + "export-extensions-configuration": "エクステンション設定のエクスポート", + "import-extensions-configuration": "エクステンション設定のインポート", + "import-extensions": "拡張機能のインポート", + "import-extension": "インポート拡張", + "export-extension": "輸出延長", + "file": "拡張機能ファイル", + "invalid-file-error": "無効な拡張ファイル" + }, + "fullscreen": { + "expand": "フルスクリーンに拡大", + "exit": "全画面表示を終了", + "toggle": "フルスクリーンモードを切り替える", + "fullscreen": "全画面表示" + }, + "function": { + "function": "関数" + }, + "grid": { + "delete-item-title": "このアイテムを削除してもよろしいですか?", + "delete-item-text": "注意してください。確認後、この項目と関連するすべてのデータは回復不能になります。", + "delete-items-title": "{ count, plural, 1 {1 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": "トップにスクロールします" + }, + "help": { + "goto-help-page": "ヘルプページに行く" + }, + "home": { + "home": "ホーム", + "profile": "プロフィール", + "logout": "ログアウト", + "menu": "メニュー", + "avatar": "アバター", + "open-user-menu": "ユーザーメニューを開く" + }, + "import": { + "no-file": "ファイルが選択されていません", + "drop-file": "JSONファイルをドロップするか、アップロードするファイルをクリックして選択します。" + }, + "item": { + "selected": "選択された" + }, + "js-func": { + "no-return-error": "関数は値を返す必要があります!", + "return-type-mismatch": "'{{type}}'タイプ!", + "tidy": "きちんとした" + }, + "key-val": { + "key": "キー", + "value": "値", + "remove-entry": "エントリを削除", + "add-entry": "エントリを追加", + "no-data": "エントリなし" + }, + "layout": { + "layout": "レイアウト", + "manage": "レイアウトの管理", + "settings": "レイアウト設定", + "color": "色", + "main": "メイン", + "right": "右", + "select": "ターゲットレイアウトを選択" + }, + "legend": { + "position": "伝説の位置", + "show-max": "最大値を表示", + "show-min": "最小値を表示する", + "show-avg": "平均値を表示", + "show-total": "合計値を表示", + "settings": "凡例の設定", + "min": "分", + "max": "最大", + "avg": "平均", + "total": "合計" + }, + "login": { + "login": "ログイン", + "request-password-reset": "リクエストパスワードのリセット", + "reset-password": "パスワードを再設定する", + "create-password": "パスワードの作成", + "passwords-mismatch-error": "入力されたパスワードは同じでなければなりません!", + "password-again": "パスワードをもう一度", + "sign-in": "サインインしてください", + "username": "ユーザー名(電子メール)", + "remember-me": "私を覚えてますか", + "forgot-password": "パスワードをお忘れですか?", + "password-reset": "パスワードのリセット", + "new-password": "新しいパスワード", + "new-password-again": "新しいパスワードを再入力", + "password-link-sent-message": "パスワードリセットリンクが正常に送信されました!", + "email": "Eメール" + }, + "position": { + "top": "上", + "bottom": "ボトム", + "left": "左", + "right": "右" + }, + "profile": { + "profile": "プロフィール", + "change-password": "パスワードを変更する", + "current-password": "現在のパスワード" + }, + "relation": { + "relations": "関係", + "direction": "方向", + "search-direction": { + "FROM": "から", + "TO": "に" + }, + "direction-type": { + "FROM": "から", + "TO": "に" + }, + "from-relations": "アウトバウンド関係", + "to-relations": "インバウンド関係", + "selected-relations": "{ count, plural, 1 {1 relation} other {# relations} }選択された", + "type": "タイプ", + "to-entity-type": "エンティティタイプへ", + "to-entity-name": "エンティティ名に", + "from-entity-type": "エンティティタイプから", + "from-entity-name": "エンティティ名から", + "to-entity": "実体へ", + "from-entity": "エンティティから", + "delete": "関係を削除する", + "relation-type": "関係タイプ", + "relation-type-required": "関係タイプが必要です。", + "any-relation-type": "いかなるタイプ", + "add": "関係を追加する", + "edit": "関係を編集する", + "delete-to-relation-title": "'{{entityName}}'?", + "delete-to-relation-text": "'{{entityName}}'現在のエンティティとは無関係です。", + "delete-to-relations-title": "{ count, plural, 1 {1 relation} other {# relations} }?", + "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を解析できません。" + }, + "rulechain": { + "rulechain": "ルールチェーン", + "rulechains": "ルールチェーン", + "root": "ルート", + "delete": "ルールチェーンの削除", + "name": "名", + "name-required": "名前は必須です。", + "description": "説明", + "add": "ルールチェーンを追加する", + "set-root": "ルールチェーンのルートを作る", + "set-root-rulechain-title": "'{{ruleChainName}}'ルート?", + "set-root-rulechain-text": "確認後、ルールチェーンはルートになり、すべての受信トランスポートメッセージを処理します。", + "delete-rulechain-title": "'{{ruleChainName}}'?", + "delete-rulechain-text": "確認後、ルールチェーンと関連するすべてのデータが回復不能になるので注意してください。", + "delete-rulechains-title": "{ count, plural, 1 {1 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": "イベント", + "system": "システム", + "import": "ルールチェーンのインポート", + "export": "ルールチェーンのエクスポート", + "export-failed-error": "{{error}}", + "create-new-rulechain": "新しいルールチェーンを作成する", + "rulechain-file": "ルールチェーンファイル", + "invalid-rulechain-file-error": "ルールチェーンをインポートできません:ルールチェーンのデータ構造が無効です。", + "copyId": "ルールチェーンIDのコピー", + "idCopiedMessage": "ルールチェーンIDがクリップボードにコピーされました", + "select-rulechain": "ルールチェーンの選択", + "no-rulechains-matching": "'{{entity}}'発見されました。", + "rulechain-required": "ルールチェーンが必要です", + "management": "ルール管理", + "debug-mode": "デバッグモード" + }, + "rulenode": { + "details": "詳細", + "events": "イベント", + "search": "検索ノード", + "open-node-library": "オープンノードライブラリ", + "add": "ルールノードを追加する", + "name": "名", + "name-required": "名前は必須です。", + "type": "タイプ", + "description": "説明", + "delete": "ルールノードを削除", + "select-all-objects": "すべてのノードと接続を選択する", + "deselect-all-objects": "すべてのノードと接続の選択を解除する", + "delete-selected-objects": "選択したノードと接続を削除する", + "delete-selected": "選択を削除します", + "select-all": "すべて選択", + "copy-selected": "選択したコピー", + "deselect-all": "すべての選択を解除", + "rulenode-details": "ルールノードの詳細", + "debug-mode": "デバッグモード", + "configuration": "構成", + "link": "リンク", + "link-details": "ルールノードのリンクの詳細", + "add-link": "リンクを追加", + "link-label": "リンクラベル", + "link-label-required": "リンクラベルが必要です。", + "custom-link-label": "カスタムリンクラベル", + "custom-link-label-required": "カスタムリンクラベルが必要です。", + "link-labels": "リンクラベル", + "link-labels-required": "リンクラベルが必要です。", + "no-link-labels-found": "リンクラベルが見つかりません", + "no-link-label-matching": "'{{label}}'見つかりません。", + "create-new-link-label": "新しいものを作成してください!", + "type-filter": "フィルタ", + "type-filter-details": "設定された条件で着信メッセージをフィルタリングする", + "type-enrichment": "豊かな", + "type-enrichment-details": "メッセージメタデータに追加情報を追加する", + "type-transformation": "変換", + "type-transformation-details": "メッセージペイロードとメタデータの変更", + "type-action": "アクション", + "type-action-details": "特別なアクションを実行する", + "type-external": "外部", + "type-external-details": "外部システムとの相互作用", + "type-rule-chain": "ルールチェーン", + "type-rule-chain-details": "受信したメッセージを指定したルールチェーンに転送する", + "type-input": "入力", + "type-input-details": "ルールチェーンの論理入力、次の関連ルールノードへの着信メッセージの転送", + "type-unknown": "未知の", + "type-unknown-details": "未解決のルールノード", + "directive-is-not-loaded": "'{{directiveName}}'利用できません。", + "ui-resources-load-error": "構成UIリソースをロードできませんでした。", + "invalid-target-rulechain": "ターゲットルールチェーンを解決できません!", + "test-script-function": "テストスクリプト機能", + "message": "メッセージ", + "message-type": "メッセージタイプ", + "select-message-type": "メッセージタイプを選択", + "message-type-required": "メッセージタイプは必須です", + "metadata": "メタデータ", + "metadata-required": "メタデータのエントリを空にすることはできません。", + "output": "出力", + "test": "テスト", + "help": "助けて" + }, + "tenant": { + "tenant": "テナント", + "tenants": "テナント", + "management": "テナント管理", + "add": "テナントを追加", + "admins": "管理者", + "manage-tenant-admins": "テナント管理者の管理", + "delete": "テナントの削除", + "add-tenant-text": "新しいテナントを追加する", + "no-tenants-text": "テナントは見つかりませんでした", + "tenant-details": "テナントの詳細", + "delete-tenant-title": "'{{tenantTitle}}'?", + "delete-tenant-text": "確認後、テナントと関連するすべてのデータが回復不能になるので注意してください。", + "delete-tenants-title": "{ count, plural, 1 {1 tenant} other {# tenants} }?", + "delete-tenants-action-title": "{ count, plural, 1 {1 tenant} other {# tenants} }", + "delete-tenants-text": "注意してください。確認後、選択されたすべてのテナントが削除され、関連するすべてのデータは回復不能になります。", + "title": "タイトル", + "title-required": "タイトルは必須です。", + "description": "説明", + "details": "詳細", + "events": "イベント", + "copyId": "テナントIDをコピーする", + "idCopiedMessage": "テナントIDがクリップボードにコピーされました", + "select-tenant": "テナントを選択", + "no-tenants-matching": "'{{entity}}'発見されました。", + "tenant-required": "テナントが必要です" + }, + "timeinterval": { + "seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }", + "minutes-interval": "{ minutes, plural, 1 {1 minute} other {# minutes} }", + "hours-interval": "{ hours, plural, 1 {1 hour} other {# hours} }", + "days-interval": "{ days, plural, 1 {1 day} other {# days} }", + "days": "日々", + "hours": "時間", + "minutes": "分", + "seconds": "秒", + "advanced": "上級" + }, + "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": "リアルタイム", + "history": "歴史", + "last-prefix": "最終", + "period": "{{ startTime }}{{ endTime }}", + "edit": "タイムウィンドウを編集", + "date-range": "期間", + "last": "最終", + "time-period": "期間" + }, + "user": { + "user": "ユーザー", + "users": "ユーザー", + "customer-users": "顧客ユーザー", + "tenant-admins": "テナント管理者", + "sys-admin": "システム管理者", + "tenant-admin": "テナント管理者", + "customer": "顧客", + "anonymous": "匿名", + "add": "ユーザーを追加する", + "delete": "ユーザーを削除", + "add-user-text": "新しいユーザーを追加", + "no-users-text": "ユーザが見つかりませんでした", + "user-details": "ユーザーの詳細", + "delete-user-title": "'{{userEmail}}'?", + "delete-user-text": "確認後、ユーザーと関連するすべてのデータが回復不能になるので注意してください。", + "delete-users-title": "{ count, plural, 1 {1 user} other {# users} }?", + "delete-users-action-title": "{ count, plural, 1 {1 user} other {# users} }", + "delete-users-text": "注意してください。確認後、選択したすべてのユーザーが削除され、関連するすべてのデータは回復不能になります。", + "activation-email-sent-message": "アクティベーション電子メールが正常に送信されました!", + "resend-activation": "アクティブ化を再送", + "email": "Eメール", + "email-required": "電子メールが必要です。", + "invalid-email-format": "メールフォーマットが無効です。", + "first-name": "ファーストネーム", + "last-name": "苗字", + "description": "説明", + "default-dashboard": "デフォルトのダッシュボード", + "always-fullscreen": "常に全画面表示", + "select-user": "ユーザーを選択", + "no-users-matching": "'{{entity}}'発見されました。", + "user-required": "ユーザーは必須です", + "activation-method": "起動方法", + "display-activation-link": "アクティブ化リンクを表示する", + "send-activation-mail": "アクティベーションメールを送信する", + "activation-link": "ユーザーアクティベーションリンク", + "activation-link-text": "activation link :", + "copy-activation-link": "アクティブ化リンクをコピーする", + "activation-link-copied-message": "ユーザーのアクティベーションリンクがクリップボードにコピーされました", + "details": "詳細" + }, + "value": { + "type": "値のタイプ", + "string": "文字列", + "string-value": "文字列値", + "integer": "整数", + "integer-value": "整数値", + "invalid-integer-value": "整数値が無効です", + "double": "ダブル", + "double-value": "二重価値", + "boolean": "ブール", + "boolean-value": "ブール値", + "false": "偽", + "true": "真", + "long": "長いです" + }, + "widget": { + "widget-library": "ウィジェットライブラリ", + "widget-bundle": "ウィジェットバンドル", + "select-widgets-bundle": "ウィジェットのバンドルを選択", + "management": "ウィジェット管理", + "editor": "ウィジェットエディタ", + "widget-type-not-found": "ウィジェットの設定を読み込む際に問題が発生しました。
おそらく関連付けられているウィジェットのタイプが削除されています。", + "widget-type-load-error": "次のエラーのためにウィジェットが読み込まれませんでした:", + "remove": "ウィジェットを削除", + "edit": "ウィジェットの編集", + "remove-widget-title": "'{{widgetTitle}}'?", + "remove-widget-text": "確認後、ウィジェットと関連するすべてのデータは回復不能になります。", + "timeseries": "時系列", + "search-data": "検索データ", + "no-data-found": "何もデータが見つかりませんでした", + "latest-values": "最新の値", + "rpc": "コントロールウィジェット", + "alarm": "アラームウィジェット", + "static": "静的ウィジェット", + "select-widget-type": "ウィジェットタイプを選択", + "missing-widget-title-error": "ウィジェットのタイトルを指定する必要があります!", + "widget-saved": "ウィジェットが保存されました", + "unable-to-save-widget-error": "ウィジェットを保存できません!ウィジェットにエラーがあります!", + "save": "ウィジェットを保存", + "saveAs": "ウィジェットを次のように保存する", + "save-widget-type-as": "ウィジェットタイプを次のように保存します", + "save-widget-type-as-text": "新しいウィジェットのタイトルを入力したり、ターゲットウィジェットのバンドルを選択してください", + "toggle-fullscreen": "フルスクリーン切り替え", + "run": "ウィジェットを実行する", + "title": "ウィジェットのタイトル", + "title-required": "ウィジェットのタイトルが必要です。", + "type": "ウィジェットタイプ", + "resources": "リソース", + "resource-url": "JavaScript / CSS URL", + "remove-resource": "リソースを削除する", + "add-resource": "リソースを追加", + "html": "HTML", + "tidy": "きちんとした", + "css": "CSS", + "settings-schema": "設定スキーマ", + "datakey-settings-schema": "データキー設定のスキーマ", + "javascript": "Javascript", + "remove-widget-type-title": "'{{widgetName}}'?", + "remove-widget-type-text": "確認後、ウィジェットのタイプと関連するすべてのデータは回復不能になります。", + "remove-widget-type": "ウィジェットタイプを削除", + "add-widget-type": "新しいウィジェットタイプを追加する", + "widget-type-load-failed-error": "ウィジェットタイプの読み込みに失敗しました!", + "widget-template-load-failed-error": "ウィジェットテンプレートを読み込めませんでした!", + "add": "ウィジェットを追加", + "undo": "ウィジェットの変更を元に戻す", + "export": "ウィジェットの書き出し" + }, + "widget-action": { + "header-button": "ウィジェットのヘッダーボタン", + "open-dashboard-state": "新しいダッシュボードの状態に移動する", + "update-dashboard-state": "現在のダッシュボードの状態を更新する", + "open-dashboard": "他のダッシュボードに移動する", + "custom": "カスタムアクション", + "target-dashboard-state": "ターゲットダッシュボードの状態", + "target-dashboard-state-required": "ターゲットダッシュボードの状態が必要です", + "set-entity-from-widget": "エンティティをウィジェットから設定する", + "target-dashboard": "ターゲットダッシュボード", + "open-right-layout": "右ダッシュボードレイアウトを開く(モバイルビュー)" + }, + "widgets-bundle": { + "current": "現在のバンドル", + "widgets-bundles": "ウィジェットバンドル", + "add": "ウィジェットのバンドルを追加", + "delete": "ウィジェットのバンドルを削除する", + "title": "タイトル", + "title-required": "タイトルは必須です。", + "add-widgets-bundle-text": "新しいウィジェットのバンドルを追加する", + "no-widgets-bundles-text": "ウィジェットバンドルが見つかりません", + "empty": "ウィジェットのバンドルが空です", + "details": "詳細", + "widgets-bundle-details": "ウィジェットのバンドルの詳細", + "delete-widgets-bundle-title": "'{{widgetsBundleTitle}}'?", + "delete-widgets-bundle-text": "確認後、ウィジェットはバンドルされ、関連するすべてのデータは回復不能になります。", + "delete-widgets-bundles-title": "{ count, plural, 1 {1 widgets bundle} other {# widgets bundles} }?", + "delete-widgets-bundles-action-title": "{ count, plural, 1 {1 widgets bundle} other {# widgets bundles} }", + "delete-widgets-bundles-text": "確認後、選択したすべてのウィジェットバンドルは削除され、関連するすべてのデータは回復不能になります。", + "no-widgets-bundles-matching": "'{{widgetsBundle}}'発見されました。", + "widgets-bundle-required": "ウィジェットバンドルが必要です。", + "system": "システム", + "import": "インポートウィジェットバンドル", + "export": "ウィジェットのエクスポートバンドル", + "export-failed-error": "{{error}}", + "create-new-widgets-bundle": "新しいウィジェットバンドルを作成する", + "widgets-bundle-file": "ウィジェットのバンドルファイル", + "invalid-widgets-bundle-file-error": "ウィジェットをインポートできません。bundle:データ構造が無効です。" + }, + "widget-config": { + "data": "データ", + "settings": "設定", + "advanced": "上級", + "title": "タイトル", + "general-settings": "一般設定", + "display-title": "タイトルを表示", + "drop-shadow": "影を落とす", + "enable-fullscreen": "フルスクリーンを有効にする", + "background-color": "背景色", + "text-color": "テキストの色", + "padding": "パディング", + "margin": "マージン", + "widget-style": "ウィジェットスタイル", + "title-style": "タイトルスタイル", + "mobile-mode-settings": "モバイルモードの設定", + "order": "注文", + "height": "高さ", + "units": "値の隣に表示する特別なシンボル", + "decimals": "浮動小数点の後の桁数", + "timewindow": "タイムウィンドウ", + "use-dashboard-timewindow": "ダッシュボードのタイムウィンドウを使用する", + "display-legend": "伝説を表示", + "datasources": "データソース", + "maximum-datasources": "{ count, plural, 1 {1 datasource is allowed.} other {# datasources are allowed} }", + "datasource-type": "タイプ", + "datasource-parameters": "パラメーター", + "remove-datasource": "データソースを削除", + "add-datasource": "データソースを追加", + "target-device": "ターゲットデバイス", + "alarm-source": "アラームソース", + "actions": "行動", + "action": "アクション", + "add-action": "アクションを追加", + "search-actions": "検索アクション", + "action-source": "アクションソース", + "action-source-required": "アクションソースが必要です。", + "action-name": "名", + "action-name-required": "アクション名は必須です。", + "action-name-not-unique": "同じ名前の別のアクションがすでに存在します。
アクション名は、同じアクションソース内で一意である必要があります。", + "action-icon": "アイコン", + "action-type": "タイプ", + "action-type-required": "アクションタイプが必要です。", + "edit-action": "アクションの編集", + "delete-action": "アクションの削除", + "delete-action-title": "ウィジェットアクションを削除する", + "delete-action-text": "'{{actionName}}'?" + }, + "widget-type": { + "import": "インポートウィジェットタイプ", + "export": "ウィジェットのタイプをエクスポートする", + "export-failed-error": "{{error}}", + "create-new-widget-type": "新しいウィジェットタイプを作成する", + "widget-type-file": "ウィジェットタイプファイル", + "invalid-widget-type-file-error": "ウィジェットタイプをインポートできません:ウィジェットタイプのデータ構造が無効です。" + }, + "widgets": { + "date-range-navigator": { + "localizationMap": { + "Sun": "日", + "Mon": "月", + "Tue": "火", + "Wed": "水", + "Thu": "木", + "Fri": "金", + "Sat": "土", + "Jan": "1月", + "Feb": "2月", + "Mar": "3月", + "Apr": "4月", + "May": "5月", + "Jun": "6月", + "Jul": "7月", + "Aug": "8月", + "Sep": "9月", + "Oct": "10月", + "Nov": "11月", + "Dec": "12月", + "January": "1月", + "February": "2月", + "March": "行進", + "April": "4月", + "June": "六月", + "July": "7月", + "August": "8月", + "September": "9月", + "October": "10月", + "November": "11月", + "December": "12月", + "Custom Date Range": "カスタム期間", + "Date Range Template": "日付範囲テンプレート", + "Today": "今日", + "Yesterday": "昨日", + "This Week": "今週", + "Last Week": "先週", + "This Month": "今月", + "Last Month": "先月", + "Year": "年", + "This Year": "今年", + "Last Year": "昨年", + "Date picker": "日付ピッカー", + "Hour": "時", + "Day": "日", + "Week": "週間", + "2 weeks": "2週間", + "Month": "月", + "3 months": "3ヶ月", + "6 months": "6ヵ月", + "Custom interval": "カスタム間隔", + "Interval": "間隔", + "Step size": "刻み幅", + "Ok": "Ok" + } + } + }, + "icon": { + "icon": "アイコン", + "select-icon": "選択アイコン", + "material-icons": "マテリアルアイコン", + "show-all": "すべてのアイコンを表示する" + }, + "custom": { + "widget-action": { + "action-cell-button": "アクションセルボタン", + "row-click": "行のクリック", + "polygon-click": "ポリゴンクリック", + "marker-click": "マーカークリック", + "tooltip-tag-action": "ツールチップのタグアクション" + } + }, + "language": { + "language": "言語", + "locales": { + "de_DE": "ドイツ語", + "fr_FR": "フランス語", + "en_US": "英語", + "ko_KR": "韓国語", + "it_IT": "イタリアの", + "zh_CN": "中国語", + "ru_RU": "ロシア", + "es_ES": "スペイン語", + "ja_JA": "日本語", + "tr_TR": "トルコ語", + "fa_IR": "ペルシャ語", + "uk_UA": "ウクライナ語", + "cs_CZ": "チェコ語で" + } + } } \ No newline at end of file diff --git a/ui/src/app/locale/locale.constant-ko_KR.json b/ui/src/app/locale/locale.constant-ko_KR.json index fde60c77bb..863a12e6de 100644 --- a/ui/src/app/locale/locale.constant-ko_KR.json +++ b/ui/src/app/locale/locale.constant-ko_KR.json @@ -83,6 +83,8 @@ "timeout-required": "제한시간을 입력해야 합니다.", "timeout-invalid": "올바른 제한시간이 아닙니다.", "enable-tls": "TLS 사용", + "tls-version" : "TLS 버전", + "enter-tls-version" : "TLS 버전을 입력하세요", "send-test-mail": "테스트 메일 보내기" }, "alarm": { diff --git a/ui/src/app/locale/locale.constant-lv_LV.json b/ui/src/app/locale/locale.constant-lv_LV.json index 4ddbb1856f..169777a31e 100644 --- a/ui/src/app/locale/locale.constant-lv_LV.json +++ b/ui/src/app/locale/locale.constant-lv_LV.json @@ -84,6 +84,8 @@ "timeout-required": "Timeout is required.", "timeout-invalid": "That doesn't look like a valid timeout.", "enable-tls": "Enable TLS", + "tls-version": "TLS version", + "enter-tls-version" : "Enter TLS version", "send-test-mail": "Send test mail" }, "alarm": { diff --git a/ui/src/app/locale/locale.constant-ru_RU.json b/ui/src/app/locale/locale.constant-ru_RU.json index 87075014ba..4ea9dac882 100644 --- a/ui/src/app/locale/locale.constant-ru_RU.json +++ b/ui/src/app/locale/locale.constant-ru_RU.json @@ -85,6 +85,8 @@ "timeout-required": "Таймаут обязателен.", "timeout-invalid": "Недействительный таймаут.", "enable-tls": "Включить TLS", + "tls-version" : "Версия TLS", + "enter-tls-version" : "Введите версию TLS", "send-test-mail": "Отправить пробное письмо", "security-settings": "Настройки безопасности", "password-policy": "Политика паролей", diff --git a/ui/src/app/locale/locale.constant-tr_TR.json b/ui/src/app/locale/locale.constant-tr_TR.json index c9c5964c52..1f5b424faf 100644 --- a/ui/src/app/locale/locale.constant-tr_TR.json +++ b/ui/src/app/locale/locale.constant-tr_TR.json @@ -83,6 +83,8 @@ "timeout-required": "Zaman aşımı değeri gerekli.", "timeout-invalid": "Bu geçerli bir zaman aşımı gibi görünmüyor.", "enable-tls": "TLS'i etkinleştir.", + "tls-version" : "TLS sürümü", + "enter-tls-version" : "TLS sürümünü girin", "send-test-mail": "Test e-postası gönder" }, "alarm": { diff --git a/ui/src/app/locale/locale.constant-uk_UA.json b/ui/src/app/locale/locale.constant-uk_UA.json index 6aa1e06e89..f1f4f981f5 100644 --- a/ui/src/app/locale/locale.constant-uk_UA.json +++ b/ui/src/app/locale/locale.constant-uk_UA.json @@ -87,6 +87,8 @@ "timeout-required": "Необхідно задати час очікування.", "timeout-invalid": "Це не схоже на правильний час очікування.", "enable-tls": "Увімкнути TLS", + "tls-version" : "Версія TLS", + "enter-tls-version" : "Вкажіть версію TLS", "send-test-mail": "Надіслати тестове повідомлення", "use-system-mail-settings": "Використовувати параметри системного поштового сервера", "mail-templates": "Шаблони електронної пошти", diff --git a/ui/src/app/locale/locale.constant-zh_CN.json b/ui/src/app/locale/locale.constant-zh_CN.json index 0c1f5f743b..d3fef3f96a 100644 --- a/ui/src/app/locale/locale.constant-zh_CN.json +++ b/ui/src/app/locale/locale.constant-zh_CN.json @@ -83,6 +83,8 @@ "timeout-required": "超时必填。", "timeout-invalid": "这看起来不像有效的超时值。", "enable-tls": "启用TLS", + "tls-version" : "TLS版本", + "enter-tls-version" : "输入TLS版本", "send-test-mail": "发送测试邮件" }, "alarm": { diff --git a/ui/src/app/locale/locale.constant-zh_TW.json b/ui/src/app/locale/locale.constant-zh_TW.json index acaa9ac53c..1444cb5cc2 100644 --- a/ui/src/app/locale/locale.constant-zh_TW.json +++ b/ui/src/app/locale/locale.constant-zh_TW.json @@ -83,6 +83,8 @@         "timeout-required": "超時必填。",         "timeout-invalid": "這看起來不像有效的超時值。",         "enable-tls": "啟用TLS", + "tls-version": "TLS版本", + "enter-tls-version" : "输入TLS版本",         "send-test-mail": "發送測試郵件"     },     "alarm": { From 436d37ff42a1d01d5ed5a5db68d83889de777623 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Sat, 15 Feb 2020 13:37:52 +0200 Subject: [PATCH 3/9] Refactoring and backward-compatiblity improvements --- .../device/DeviceActorMessageProcessor.java | 32 ++--------- .../service/mail/DefaultMailService.java | 9 ++- .../thingsboard/server/utils/JsonUtils.java | 55 +++++++++++++++++++ 3 files changed, 67 insertions(+), 29 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/utils/JsonUtils.java diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java index 1be7e18aab..acf1a3161c 100644 --- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java +++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java @@ -69,6 +69,7 @@ import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; import org.thingsboard.server.service.rpc.ToDeviceRpcRequestActorMsg; import org.thingsboard.server.service.rpc.ToServerRpcResponseActorMsg; import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper; +import org.thingsboard.server.utils.JsonUtils; import javax.annotation.Nullable; import java.util.ArrayList; @@ -102,7 +103,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { private final Map toServerRpcPendingMap; private final Gson gson = new Gson(); - private final JsonParser jsonParser = new JsonParser(); private int rpcSeq = 0; private String deviceName; @@ -327,7 +327,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { } private void handlePostAttributesRequest(ActorContext context, SessionInfoProto sessionInfo, PostAttributeMsg postAttributes) { - JsonObject json = getJsonObject(postAttributes.getKvList()); + JsonObject json = JsonUtils.getJsonObject(postAttributes.getKvList()); TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), deviceId, defaultMetaData.copy(), TbMsgDataType.JSON, gson.toJson(json), null, null, 0L); pushToRuleEngine(context, tbMsg); @@ -335,7 +335,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { private void handlePostTelemetryRequest(ActorContext context, SessionInfoProto sessionInfo, PostTelemetryMsg postTelemetry) { for (TsKvListProto tsKv : postTelemetry.getTsKvListList()) { - JsonObject json = getJsonObject(tsKv.getKvList()); + JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); TbMsgMetaData metaData = defaultMetaData.copy(); metaData.putValue("ts", tsKv.getTs() + ""); TbMsg tbMsg = new TbMsg(UUIDs.timeBased(), SessionMsgType.POST_TELEMETRY_REQUEST.name(), deviceId, metaData, TbMsgDataType.JSON, gson.toJson(json), null, null, 0L); @@ -347,7 +347,7 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { UUID sessionId = getSessionId(sessionInfo); JsonObject json = new JsonObject(); json.addProperty("method", request.getMethodName()); - json.add("params", jsonParser.parse(request.getParams())); + json.add("params", JsonUtils.parse(request.getParams())); TbMsgMetaData requestMetaData = defaultMetaData.copy(); requestMetaData.putValue("requestId", Integer.toString(request.getRequestId())); @@ -551,30 +551,6 @@ class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcessor { this.defaultMetaData.putValue("deviceType", deviceType); } - private JsonObject getJsonObject(List tsKv) { - JsonObject json = new JsonObject(); - for (KeyValueProto kv : tsKv) { - switch (kv.getType()) { - case BOOLEAN_V: - json.addProperty(kv.getKey(), kv.getBoolV()); - break; - case LONG_V: - json.addProperty(kv.getKey(), kv.getLongV()); - break; - case DOUBLE_V: - json.addProperty(kv.getKey(), kv.getDoubleV()); - break; - case STRING_V: - json.addProperty(kv.getKey(), kv.getStringV()); - break; - case JSON_V: - json.add(kv.getKey(), jsonParser.parse(kv.getJsonV())); - break; - } - } - return json; - } - private void sendToTransport(GetAttributeResponseMsg responseMsg, SessionInfoProto sessionInfo) { DeviceActorToTransportMsg msg = DeviceActorToTransportMsg.newBuilder() .setSessionIdMSB(sessionInfo.getSessionIdMSB()) diff --git a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java index 9d2ae3beac..baa0d417a3 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/DefaultMailService.java @@ -103,7 +103,14 @@ public class DefaultMailService implements MailService { javaMailProperties.put(MAIL_PROP + protocol + ".port", jsonConfig.get("smtpPort").asText()); javaMailProperties.put(MAIL_PROP + protocol + ".timeout", jsonConfig.get("timeout").asText()); javaMailProperties.put(MAIL_PROP + protocol + ".auth", String.valueOf(StringUtils.isNotEmpty(jsonConfig.get("username").asText()))); - boolean enableTls = jsonConfig.has("enableTls") && jsonConfig.get("enableTls").booleanValue(); + boolean enableTls = false; + if (jsonConfig.has("enableTls")) { + if (jsonConfig.get("enableTls").isBoolean() && jsonConfig.get("enableTls").booleanValue()) { + enableTls = true; + } else if (jsonConfig.get("enableTls").isTextual()) { + enableTls = "true".equalsIgnoreCase(jsonConfig.get("enableTls").asText()); + } + } javaMailProperties.put(MAIL_PROP + protocol + ".starttls.enable", enableTls); if (enableTls && jsonConfig.has("tlsVersion") && StringUtils.isNoneEmpty(jsonConfig.get("tlsVersion").asText())) { javaMailProperties.put(MAIL_PROP + protocol + ".ssl.protocols", jsonConfig.get("tlsVersion").asText()); diff --git a/application/src/main/java/org/thingsboard/server/utils/JsonUtils.java b/application/src/main/java/org/thingsboard/server/utils/JsonUtils.java new file mode 100644 index 0000000000..5621362c09 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/utils/JsonUtils.java @@ -0,0 +1,55 @@ +/** + * Copyright © 2016-2020 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.utils; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto; +import java.util.List; + +public class JsonUtils { + + private static final JsonParser jsonParser = new JsonParser(); + + public static JsonObject getJsonObject(List tsKv) { + JsonObject json = new JsonObject(); + for (KeyValueProto kv : tsKv) { + switch (kv.getType()) { + case BOOLEAN_V: + json.addProperty(kv.getKey(), kv.getBoolV()); + break; + case LONG_V: + json.addProperty(kv.getKey(), kv.getLongV()); + break; + case DOUBLE_V: + json.addProperty(kv.getKey(), kv.getDoubleV()); + break; + case STRING_V: + json.addProperty(kv.getKey(), kv.getStringV()); + break; + case JSON_V: + json.add(kv.getKey(), jsonParser.parse(kv.getJsonV())); + break; + } + } + return json; + } + + public static JsonElement parse(String params) { + return jsonParser.parse(params); + } +} From a2b7e1c098eccff6ae643d7259ff715483434597 Mon Sep 17 00:00:00 2001 From: Yevhen Bondarenko <56396344+YevhenBondarenko@users.noreply.github.com> Date: Mon, 17 Feb 2020 18:11:09 +0200 Subject: [PATCH 4/9] DB upgrade script for JSON support feature * Added upgrade * Added upgrade call for attributes from ThingsboardInstallService * refactored cassandra upgrade services --- .../install/ThingsboardInstallService.java | 1 + ...stractCassandraDatabaseUpgradeService.java | 48 ++++++++++++++++ .../CassandraDatabaseUpgradeService.java | 47 +++++++--------- .../CassandraTsDatabaseUpgradeService.java | 56 +++++++++++++++++++ .../install/PsqlTsDatabaseUpgradeService.java | 3 + .../install/SqlDatabaseUpgradeService.java | 10 ++++ .../TimescaleTsDatabaseUpgradeService.java | 3 + 7 files changed, 140 insertions(+), 28 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/install/AbstractCassandraDatabaseUpgradeService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java 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 14d9ff821f..7ef4363e77 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -136,6 +136,7 @@ public class ThingsboardInstallService { log.info("Upgrading ThingsBoard from version 2.4.3 to 2.5 ..."); databaseTsUpgradeService.upgradeDatabase("2.4.3"); + databaseEntitiesUpgradeService.upgradeDatabase("2.4.3"); log.info("Updating system data..."); diff --git a/application/src/main/java/org/thingsboard/server/service/install/AbstractCassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/AbstractCassandraDatabaseUpgradeService.java new file mode 100644 index 0000000000..603158314a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/install/AbstractCassandraDatabaseUpgradeService.java @@ -0,0 +1,48 @@ +/** + * Copyright © 2016-2020 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.install; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.thingsboard.server.dao.cassandra.CassandraCluster; +import org.thingsboard.server.dao.cassandra.CassandraInstallCluster; +import org.thingsboard.server.service.install.cql.CQLStatementsParser; + +import java.nio.file.Path; +import java.util.List; + +@Slf4j +public abstract class AbstractCassandraDatabaseUpgradeService { + @Autowired + protected CassandraCluster cluster; + + @Autowired + @Qualifier("CassandraInstallCluster") + private CassandraInstallCluster installCluster; + + protected void loadCql(Path cql) throws Exception { + List statements = new CQLStatementsParser(cql).getStatements(); + statements.forEach(statement -> { + installCluster.getSession().execute(statement); + try { + Thread.sleep(2500); + } catch (InterruptedException e) { + } + }); + Thread.sleep(5000); + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java index 7e05179be0..721d43bf9f 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraDatabaseUpgradeService.java @@ -19,20 +19,15 @@ import com.datastax.driver.core.KeyspaceMetadata; import com.datastax.driver.core.exceptions.InvalidQueryException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; -import org.thingsboard.server.dao.cassandra.CassandraCluster; -import org.thingsboard.server.dao.cassandra.CassandraInstallCluster; import org.thingsboard.server.dao.dashboard.DashboardService; import org.thingsboard.server.dao.util.NoSqlDao; -import org.thingsboard.server.service.install.cql.CQLStatementsParser; import org.thingsboard.server.service.install.cql.CassandraDbHelper; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.List; import static org.thingsboard.server.service.install.DatabaseHelper.ADDITIONAL_INFO; import static org.thingsboard.server.service.install.DatabaseHelper.ASSET; @@ -59,17 +54,10 @@ import static org.thingsboard.server.service.install.DatabaseHelper.TYPE; @NoSqlDao @Profile("install") @Slf4j -public class CassandraDatabaseUpgradeService implements DatabaseEntitiesUpgradeService { +public class CassandraDatabaseUpgradeService extends AbstractCassandraDatabaseUpgradeService implements DatabaseEntitiesUpgradeService { private static final String SCHEMA_UPDATE_CQL = "schema_update.cql"; - @Autowired - private CassandraCluster cluster; - - @Autowired - @Qualifier("CassandraInstallCluster") - private CassandraInstallCluster installCluster; - @Autowired private DashboardService dashboardService; @@ -264,7 +252,8 @@ public class CassandraDatabaseUpgradeService implements DatabaseEntitiesUpgradeS try { cluster.getSession().execute(updateDeviceTableStmt); Thread.sleep(2500); - } catch (InvalidQueryException e) {} + } catch (InvalidQueryException e) { + } log.info("Schema updated."); break; case "2.4.1": @@ -275,7 +264,8 @@ public class CassandraDatabaseUpgradeService implements DatabaseEntitiesUpgradeS cluster.getSession().execute(updateAssetTableStmt); Thread.sleep(2500); log.info("Assets updated."); - } catch (InvalidQueryException e) {} + } catch (InvalidQueryException e) { + } log.info("Schema updated."); break; case "2.4.2": @@ -286,24 +276,25 @@ public class CassandraDatabaseUpgradeService implements DatabaseEntitiesUpgradeS cluster.getSession().execute(updateAlarmTableStmt); Thread.sleep(2500); log.info("Alarms updated."); - } catch (InvalidQueryException e) {} + } catch (InvalidQueryException e) { + } + log.info("Schema updated."); + break; + case "2.4.3": + log.info("Updating schema ..."); + String updateAttributeKvTableStmt = "alter table attributes_kv_cf add json_v text"; + try { + log.info("Updating attributes ..."); + cluster.getSession().execute(updateAttributeKvTableStmt); + Thread.sleep(2500); + log.info("Attributes updated."); + } catch (InvalidQueryException e) { + } log.info("Schema updated."); break; default: throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); } - - } - - private void loadCql(Path cql) throws Exception { - List statements = new CQLStatementsParser(cql).getStatements(); - statements.forEach(statement -> { - installCluster.getSession().execute(statement); - try { - Thread.sleep(2500); - } catch (InterruptedException e) {} - }); - Thread.sleep(5000); } } diff --git a/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java new file mode 100644 index 0000000000..4bc68e92bc --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/install/CassandraTsDatabaseUpgradeService.java @@ -0,0 +1,56 @@ +/** + * Copyright © 2016-2020 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.install; + +import com.datastax.driver.core.exceptions.InvalidQueryException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; +import org.thingsboard.server.dao.util.NoSqlTsDao; + +@Service +@NoSqlTsDao +@Profile("install") +@Slf4j +public class CassandraTsDatabaseUpgradeService extends AbstractCassandraDatabaseUpgradeService implements DatabaseTsUpgradeService { + + @Override + public void upgradeDatabase(String fromVersion) throws Exception { + switch (fromVersion) { + case "2.4.3": + log.info("Updating schema ..."); + String updateTsKvTableStmt = "alter table ts_kv_cf add json_v text"; + String updateTsKvLatestTableStmt = "alter table ts_kv_latest_cf add json_v text"; + + try { + log.info("Updating ts ..."); + cluster.getSession().execute(updateTsKvTableStmt); + Thread.sleep(2500); + log.info("Ts updated."); + log.info("Updating ts latest ..."); + cluster.getSession().execute(updateTsKvLatestTableStmt); + Thread.sleep(2500); + log.info("Ts latest updated."); + } catch (InvalidQueryException e) { + } + log.info("Schema updated."); + break; + default: + throw new RuntimeException("Unable to upgrade Cassandra database, unsupported fromVersion: " + fromVersion); + } + } + +} diff --git a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java index 2b1cbd053d..eb951ed9ae 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/PsqlTsDatabaseUpgradeService.java @@ -101,6 +101,9 @@ public class PsqlTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgradeSe executeDropStatement(conn, DROP_FUNCTION_CREATE_NEW_TS_KV_LATEST_TABLE); executeDropStatement(conn, DROP_FUNCTION_INSERT_INTO_TS_KV_LATEST); + executeQuery(conn, "ALTER TABLE ts_kv ADD COLUMN json_v json;"); + executeQuery(conn, "ALTER TABLE ts_kv_latest ADD COLUMN json_v json;"); + log.info("schema timeseries updated!"); } } diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index d018c7fcef..aa5d3e3d95 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -207,6 +207,16 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService log.info("Schema updated."); } break; + case "2.4.3": + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + log.info("Updating schema ..."); + try { + conn.createStatement().execute("ALTER TABLE attribute_kv ADD COLUMN json_v json;"); + } catch (Exception e) { + } + log.info("Schema updated."); + } + break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); } diff --git a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java index 84adbbc140..0c57e2e9bd 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/TimescaleTsDatabaseUpgradeService.java @@ -104,6 +104,9 @@ public class TimescaleTsDatabaseUpgradeService extends AbstractSqlTsDatabaseUpgr executeDropStatement(conn, DROP_FUNCTION_INSERT_INTO_TENANT_TS_KV); executeDropStatement(conn, DROP_FUNCTION_INSERT_INTO_TS_KV_LATEST); + executeQuery(conn, "ALTER TABLE ts_kv ADD COLUMN json_v json;"); + executeQuery(conn, "ALTER TABLE ts_kv_latest ADD COLUMN json_v json;"); + log.info("schema timeseries updated!"); } } From ec4362a8d29a4db303e5f4d9c2991c1ee3dd2c42 Mon Sep 17 00:00:00 2001 From: YevhenBondarenko Date: Mon, 17 Feb 2020 12:00:37 +0200 Subject: [PATCH 5/9] added list with tls versions for outgoing-mail-settings --- ui/src/app/admin/admin.controller.js | 2 ++ ui/src/app/admin/outgoing-mail-settings.tpl.html | 6 +++++- ui/src/app/locale/locale.constant-cs_CZ.json | 1 - ui/src/app/locale/locale.constant-de_DE.json | 1 - ui/src/app/locale/locale.constant-el_GR.json | 1 - ui/src/app/locale/locale.constant-en_US.json | 1 - ui/src/app/locale/locale.constant-es_ES.json | 1 - ui/src/app/locale/locale.constant-fa_IR.json | 1 - ui/src/app/locale/locale.constant-fr_FR.json | 1 - ui/src/app/locale/locale.constant-it_IT.json | 1 - ui/src/app/locale/locale.constant-ja_JA.json | 1 - ui/src/app/locale/locale.constant-ko_KR.json | 1 - ui/src/app/locale/locale.constant-lv_LV.json | 1 - ui/src/app/locale/locale.constant-ru_RU.json | 1 - ui/src/app/locale/locale.constant-tr_TR.json | 1 - ui/src/app/locale/locale.constant-uk_UA.json | 1 - ui/src/app/locale/locale.constant-zh_CN.json | 1 - ui/src/app/locale/locale.constant-zh_TW.json | 1 - 18 files changed, 7 insertions(+), 17 deletions(-) diff --git a/ui/src/app/admin/admin.controller.js b/ui/src/app/admin/admin.controller.js index 256faf0245..fa1c9920ee 100644 --- a/ui/src/app/admin/admin.controller.js +++ b/ui/src/app/admin/admin.controller.js @@ -25,6 +25,8 @@ export default function AdminController(adminService, toast, $scope, $rootScope, return protocol; }); + vm.tlsVersions = ['TLSv1.0', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3']; + $translate('admin.test-mail-sent').then(function (translation) { vm.testMailSent = translation; }, function (translationId) { diff --git a/ui/src/app/admin/outgoing-mail-settings.tpl.html b/ui/src/app/admin/outgoing-mail-settings.tpl.html index 20f988f866..edb2e4e520 100644 --- a/ui/src/app/admin/outgoing-mail-settings.tpl.html +++ b/ui/src/app/admin/outgoing-mail-settings.tpl.html @@ -82,7 +82,11 @@ aria-label="{{ 'admin.enable-tls' | translate }}" ng-model="vm.settings.jsonValue.enableTls">{{ 'admin.enable-tls' | translate }} - + + + {{tlsVersion}} + + diff --git a/ui/src/app/locale/locale.constant-cs_CZ.json b/ui/src/app/locale/locale.constant-cs_CZ.json index 59d6fd5ece..76311883a8 100644 --- a/ui/src/app/locale/locale.constant-cs_CZ.json +++ b/ui/src/app/locale/locale.constant-cs_CZ.json @@ -84,7 +84,6 @@ "timeout-invalid": "Tohle nevypadá jako platný časový limit.", "enable-tls": "Povolit TLS", "tls-version": "Verze TLS", - "enter-tls-version" : "Zadejte verzi TLS", "send-test-mail": "Odeslat testovací zprávu" }, "alarm": { diff --git a/ui/src/app/locale/locale.constant-de_DE.json b/ui/src/app/locale/locale.constant-de_DE.json index 7fb595a882..57d64c104e 100644 --- a/ui/src/app/locale/locale.constant-de_DE.json +++ b/ui/src/app/locale/locale.constant-de_DE.json @@ -84,7 +84,6 @@ "timeout-invalid": "Das ist keine gültige Wartezeit.", "enable-tls": "TLS aktivieren", "tls-version" : "TLS-Version", - "enter-tls-version" : "Geben Sie die TLS-Version ein", "send-test-mail": "Test E-Mail senden", "security-settings": "Sicherheitseinstellungen", "password-policy": "Kennwortrichtlinie", diff --git a/ui/src/app/locale/locale.constant-el_GR.json b/ui/src/app/locale/locale.constant-el_GR.json index 7669e705b9..1817c64509 100644 --- a/ui/src/app/locale/locale.constant-el_GR.json +++ b/ui/src/app/locale/locale.constant-el_GR.json @@ -89,7 +89,6 @@ "timeout-invalid": "Αυτή δε φαίνεται να είναι μια έγκυρη τιμή timeout.", "enable-tls": "Ενεργοποίηση TLS", "tls-version": "Έκδοση TLS", - "enter-tls-version" : "Εισαγάγετε την έκδοση TLS", "send-test-mail": "Αποστολή δοκιμαστικού μηνύματος", "use-system-mail-settings": "Χρήση των ρυθμίσεων διακομιστή αλληλογραφίας συστήματος", "mail-templates": "Πρότυπα αλληλογραφίας", diff --git a/ui/src/app/locale/locale.constant-en_US.json b/ui/src/app/locale/locale.constant-en_US.json index cfadab8e1f..52baeda729 100644 --- a/ui/src/app/locale/locale.constant-en_US.json +++ b/ui/src/app/locale/locale.constant-en_US.json @@ -87,7 +87,6 @@ "timeout-invalid": "That doesn't look like a valid timeout.", "enable-tls": "Enable TLS", "tls-version": "TLS version", - "enter-tls-version" : "Enter TLS version", "send-test-mail": "Send test mail", "security-settings": "Security settings", "password-policy": "Password policy", diff --git a/ui/src/app/locale/locale.constant-es_ES.json b/ui/src/app/locale/locale.constant-es_ES.json index 4b76cd7b70..0473b7df8f 100644 --- a/ui/src/app/locale/locale.constant-es_ES.json +++ b/ui/src/app/locale/locale.constant-es_ES.json @@ -86,7 +86,6 @@ "timeout-invalid": "Eso no parece un tiempo de espera válido.", "enable-tls": "Habilitar TLS", "tls-version": "Versión TLS", - "enter-tls-version" : "Ingrese la versión de TLS", "send-test-mail": "Enviar correo de prueba", "password-policy": "Política de contraseñas", "security-settings": "Configuraciones de seguridad", diff --git a/ui/src/app/locale/locale.constant-fa_IR.json b/ui/src/app/locale/locale.constant-fa_IR.json index 968769fe29..4c83a7eea9 100644 --- a/ui/src/app/locale/locale.constant-fa_IR.json +++ b/ui/src/app/locale/locale.constant-fa_IR.json @@ -84,7 +84,6 @@ "timeout-invalid": ".مهلت، به نظر نمي آيد معتبر باشد", "enable-tls": "TLS فعال سازي", "tls-version": "نسخه TLS", - "enter-tls-version" : "نسخه TLS را وارد کنید", "send-test-mail": "ارسال پيام آزمايشي" }, "alarm": { diff --git a/ui/src/app/locale/locale.constant-fr_FR.json b/ui/src/app/locale/locale.constant-fr_FR.json index c12418652a..f155563742 100644 --- a/ui/src/app/locale/locale.constant-fr_FR.json +++ b/ui/src/app/locale/locale.constant-fr_FR.json @@ -57,7 +57,6 @@ "base-url-required": "L'URL de base est requise.", "enable-tls": "Activer TLS", "tls-version": "Version TLS", - "enter-tls-version" : "Entrez la version TLS", "general": "Général", "general-settings": "Paramètres généraux", "mail-from": "Mail de", diff --git a/ui/src/app/locale/locale.constant-it_IT.json b/ui/src/app/locale/locale.constant-it_IT.json index 8b53c91983..f3a6be8d96 100644 --- a/ui/src/app/locale/locale.constant-it_IT.json +++ b/ui/src/app/locale/locale.constant-it_IT.json @@ -85,7 +85,6 @@ "timeout-invalid": "Timeout non valido.", "enable-tls": "Abilita TLS", "tls-version" : "Versione TLS", - "enter-tls-version" : "Inserisci la versione TLS", "send-test-mail": "Invia mail di test", "security-settings": "Settaggi di sicurezza", "password-policy": "Politica password", diff --git a/ui/src/app/locale/locale.constant-ja_JA.json b/ui/src/app/locale/locale.constant-ja_JA.json index 6e60c0ed95..4a789094f5 100644 --- a/ui/src/app/locale/locale.constant-ja_JA.json +++ b/ui/src/app/locale/locale.constant-ja_JA.json @@ -84,7 +84,6 @@ "timeout-invalid": "それは有効なタイムアウトのようには見えません。", "enable-tls": "TLSを有効にする", "tls-version": "TLSバージョン", - "enter-tls-version" : "TLSバージョンを入力してください", "send-test-mail": "テストメールを送信する" }, "alarm": { diff --git a/ui/src/app/locale/locale.constant-ko_KR.json b/ui/src/app/locale/locale.constant-ko_KR.json index 863a12e6de..f9395b1dd6 100644 --- a/ui/src/app/locale/locale.constant-ko_KR.json +++ b/ui/src/app/locale/locale.constant-ko_KR.json @@ -84,7 +84,6 @@ "timeout-invalid": "올바른 제한시간이 아닙니다.", "enable-tls": "TLS 사용", "tls-version" : "TLS 버전", - "enter-tls-version" : "TLS 버전을 입력하세요", "send-test-mail": "테스트 메일 보내기" }, "alarm": { diff --git a/ui/src/app/locale/locale.constant-lv_LV.json b/ui/src/app/locale/locale.constant-lv_LV.json index 169777a31e..08e7d34b7f 100644 --- a/ui/src/app/locale/locale.constant-lv_LV.json +++ b/ui/src/app/locale/locale.constant-lv_LV.json @@ -85,7 +85,6 @@ "timeout-invalid": "That doesn't look like a valid timeout.", "enable-tls": "Enable TLS", "tls-version": "TLS version", - "enter-tls-version" : "Enter TLS version", "send-test-mail": "Send test mail" }, "alarm": { diff --git a/ui/src/app/locale/locale.constant-ru_RU.json b/ui/src/app/locale/locale.constant-ru_RU.json index 4ea9dac882..f4ed6d5c93 100644 --- a/ui/src/app/locale/locale.constant-ru_RU.json +++ b/ui/src/app/locale/locale.constant-ru_RU.json @@ -86,7 +86,6 @@ "timeout-invalid": "Недействительный таймаут.", "enable-tls": "Включить TLS", "tls-version" : "Версия TLS", - "enter-tls-version" : "Введите версию TLS", "send-test-mail": "Отправить пробное письмо", "security-settings": "Настройки безопасности", "password-policy": "Политика паролей", diff --git a/ui/src/app/locale/locale.constant-tr_TR.json b/ui/src/app/locale/locale.constant-tr_TR.json index 1f5b424faf..a9dd8e7697 100644 --- a/ui/src/app/locale/locale.constant-tr_TR.json +++ b/ui/src/app/locale/locale.constant-tr_TR.json @@ -84,7 +84,6 @@ "timeout-invalid": "Bu geçerli bir zaman aşımı gibi görünmüyor.", "enable-tls": "TLS'i etkinleştir.", "tls-version" : "TLS sürümü", - "enter-tls-version" : "TLS sürümünü girin", "send-test-mail": "Test e-postası gönder" }, "alarm": { diff --git a/ui/src/app/locale/locale.constant-uk_UA.json b/ui/src/app/locale/locale.constant-uk_UA.json index f1f4f981f5..53d16bea5e 100644 --- a/ui/src/app/locale/locale.constant-uk_UA.json +++ b/ui/src/app/locale/locale.constant-uk_UA.json @@ -88,7 +88,6 @@ "timeout-invalid": "Це не схоже на правильний час очікування.", "enable-tls": "Увімкнути TLS", "tls-version" : "Версія TLS", - "enter-tls-version" : "Вкажіть версію TLS", "send-test-mail": "Надіслати тестове повідомлення", "use-system-mail-settings": "Використовувати параметри системного поштового сервера", "mail-templates": "Шаблони електронної пошти", diff --git a/ui/src/app/locale/locale.constant-zh_CN.json b/ui/src/app/locale/locale.constant-zh_CN.json index d3fef3f96a..634aae3f1f 100644 --- a/ui/src/app/locale/locale.constant-zh_CN.json +++ b/ui/src/app/locale/locale.constant-zh_CN.json @@ -84,7 +84,6 @@ "timeout-invalid": "这看起来不像有效的超时值。", "enable-tls": "启用TLS", "tls-version" : "TLS版本", - "enter-tls-version" : "输入TLS版本", "send-test-mail": "发送测试邮件" }, "alarm": { diff --git a/ui/src/app/locale/locale.constant-zh_TW.json b/ui/src/app/locale/locale.constant-zh_TW.json index 1444cb5cc2..52637b7c82 100644 --- a/ui/src/app/locale/locale.constant-zh_TW.json +++ b/ui/src/app/locale/locale.constant-zh_TW.json @@ -84,7 +84,6 @@         "timeout-invalid": "這看起來不像有效的超時值。",         "enable-tls": "啟用TLS", "tls-version": "TLS版本", - "enter-tls-version" : "输入TLS版本",         "send-test-mail": "發送測試郵件"     },     "alarm": { From 90e84b77ebd086977741187fdc9ee096f4a45e4a Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 17 Feb 2020 19:05:30 +0200 Subject: [PATCH 6/9] Fix for Cassandra Dockerfile --- msa/tb/docker-cassandra/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msa/tb/docker-cassandra/Dockerfile b/msa/tb/docker-cassandra/Dockerfile index d1dfc21973..27b95a4c27 100644 --- a/msa/tb/docker-cassandra/Dockerfile +++ b/msa/tb/docker-cassandra/Dockerfile @@ -19,7 +19,7 @@ FROM thingsboard/openjdk8 RUN apt-get update RUN apt-get install -y curl nmap procps RUN echo 'deb http://www.apache.org/dist/cassandra/debian 311x main' | tee --append /etc/apt/sources.list.d/cassandra.list > /dev/null -RUN curl https://www.apache.org/dist/cassandra/KEYS | apt-key add - +RUN wget -qO - https://www.apache.org/dist/cassandra/KEYS | apt-key add - RUN apt-get update RUN apt-get install -y cassandra cassandra-tools RUN update-rc.d cassandra disable From edb21ae0eba9dae5c46fd27f08d96e0fdc38a2fb Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 17 Feb 2020 19:47:07 +0200 Subject: [PATCH 7/9] Dockerfile fix --- msa/tb/docker-cassandra/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/msa/tb/docker-cassandra/Dockerfile b/msa/tb/docker-cassandra/Dockerfile index 27b95a4c27..f8509487b3 100644 --- a/msa/tb/docker-cassandra/Dockerfile +++ b/msa/tb/docker-cassandra/Dockerfile @@ -17,6 +17,7 @@ FROM thingsboard/openjdk8 RUN apt-get update +RUN apt-get install apt-transport-https ca-certificates RUN apt-get install -y curl nmap procps RUN echo 'deb http://www.apache.org/dist/cassandra/debian 311x main' | tee --append /etc/apt/sources.list.d/cassandra.list > /dev/null RUN wget -qO - https://www.apache.org/dist/cassandra/KEYS | apt-key add - From a32e8b7342aa3b1e2b048e8a9b7b026980bd435a Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Mon, 17 Feb 2020 20:05:58 +0200 Subject: [PATCH 8/9] Dockerfile fix --- msa/tb/docker-cassandra/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/msa/tb/docker-cassandra/Dockerfile b/msa/tb/docker-cassandra/Dockerfile index f8509487b3..9f87819909 100644 --- a/msa/tb/docker-cassandra/Dockerfile +++ b/msa/tb/docker-cassandra/Dockerfile @@ -17,7 +17,7 @@ FROM thingsboard/openjdk8 RUN apt-get update -RUN apt-get install apt-transport-https ca-certificates +RUN apt-get install -y apt-transport-https ca-certificates RUN apt-get install -y curl nmap procps RUN echo 'deb http://www.apache.org/dist/cassandra/debian 311x main' | tee --append /etc/apt/sources.list.d/cassandra.list > /dev/null RUN wget -qO - https://www.apache.org/dist/cassandra/KEYS | apt-key add - From 4c3d6a3c87ae8c25429c9a049ebed033017ea2a0 Mon Sep 17 00:00:00 2001 From: Andrii Shvaika Date: Tue, 18 Feb 2020 10:07:46 +0200 Subject: [PATCH 9/9] Fixed Cassandra installation script --- msa/tb/docker-cassandra/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/msa/tb/docker-cassandra/Dockerfile b/msa/tb/docker-cassandra/Dockerfile index 9f87819909..3a761398fd 100644 --- a/msa/tb/docker-cassandra/Dockerfile +++ b/msa/tb/docker-cassandra/Dockerfile @@ -17,10 +17,9 @@ FROM thingsboard/openjdk8 RUN apt-get update -RUN apt-get install -y apt-transport-https ca-certificates RUN apt-get install -y curl nmap procps RUN echo 'deb http://www.apache.org/dist/cassandra/debian 311x main' | tee --append /etc/apt/sources.list.d/cassandra.list > /dev/null -RUN wget -qO - https://www.apache.org/dist/cassandra/KEYS | apt-key add - +RUN curl -L https://www.apache.org/dist/cassandra/KEYS | apt-key add - RUN apt-get update RUN apt-get install -y cassandra cassandra-tools RUN update-rc.d cassandra disable