diff --git a/libnymea-core/jsonrpc/actionhandler.cpp b/libnymea-core/jsonrpc/actionhandler.cpp index 4a787aeb..d0616441 100644 --- a/libnymea-core/jsonrpc/actionhandler.cpp +++ b/libnymea-core/jsonrpc/actionhandler.cpp @@ -100,7 +100,7 @@ JsonReply *ActionHandler::GetActionType(const QVariantMap ¶ms) const foreach (const ActionType &actionType, deviceClass.actionTypes()) { if (actionType.id() == actionTypeId) { QVariantMap data = statusToReply(DeviceManager::DeviceErrorNoError); - data.insert("actionType", JsonTypes::packActionType(actionType)); + data.insert("actionType", JsonTypes::packActionType(actionType, deviceClass.pluginId(), params.value("locale").toLocale())); return createReply(data); } } diff --git a/libnymea-core/jsonrpc/configurationhandler.cpp b/libnymea-core/jsonrpc/configurationhandler.cpp index 2d8e077f..6e5c8869 100644 --- a/libnymea-core/jsonrpc/configurationhandler.cpp +++ b/libnymea-core/jsonrpc/configurationhandler.cpp @@ -75,7 +75,7 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent): setReturns("GetTimeZones", returns); params.clear(); returns.clear(); - setDescription("GetAvailableLanguages", "Returns a list of locale codes available for the server. i.e. en_US, de_AT"); + setDescription("GetAvailableLanguages", "DEPRECATED - Use the locale property in the Handshake message instead - Returns a list of locale codes available for the server. i.e. en_US, de_AT"); setParams("GetAvailableLanguages", params); returns.insert("languages", QVariantList() << JsonTypes::basicTypeToString(JsonTypes::String)); setReturns("GetAvailableLanguages", returns); @@ -122,7 +122,7 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent): setReturns("SetTimeZone", returns); params.clear(); returns.clear(); - setDescription("SetLanguage", "Sets the server language to the given language. See also: \"GetAvailableLanguages\""); + setDescription("SetLanguage", "DEPRECATED - Use the locale property in the Handshake message instead - Sets the server language to the given language. See also: \"GetAvailableLanguages\""); params.insert("language", JsonTypes::basicTypeToString(JsonTypes::String)); setParams("SetLanguage", params); returns.insert("configurationError", JsonTypes::configurationErrorRef()); @@ -295,7 +295,7 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent): connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::timeZoneChanged, this, &ConfigurationHandler::onBasicConfigurationChanged); connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::localeChanged, this, &ConfigurationHandler::onBasicConfigurationChanged); connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::debugServerEnabledChanged, this, &ConfigurationHandler::onBasicConfigurationChanged); - connect(NymeaCore::instance()->deviceManager(), &DeviceManager::languageUpdated, this, &ConfigurationHandler::onLanguageChanged); + connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::localeChanged, this, &ConfigurationHandler::onLanguageChanged); connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::tcpServerConfigurationChanged, this, &ConfigurationHandler::onTcpServerConfigurationChanged); connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::tcpServerConfigurationRemoved, this, &ConfigurationHandler::onTcpServerConfigurationRemoved); connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::webServerConfigurationChanged, this, &ConfigurationHandler::onWebServerConfigurationChanged); diff --git a/libnymea-core/jsonrpc/devicehandler.cpp b/libnymea-core/jsonrpc/devicehandler.cpp index 811a7b9f..9bdee4bd 100644 --- a/libnymea-core/jsonrpc/devicehandler.cpp +++ b/libnymea-core/jsonrpc/devicehandler.cpp @@ -60,6 +60,7 @@ #include "types/deviceclass.h" #include "plugin/device.h" #include "plugin/deviceplugin.h" +#include "translator.h" #include @@ -319,14 +320,14 @@ JsonReply* DeviceHandler::GetSupportedVendors(const QVariantMap ¶ms) const Q_UNUSED(params) QVariantMap returns; - returns.insert("vendors", JsonTypes::packSupportedVendors()); + returns.insert("vendors", JsonTypes::packSupportedVendors(params.value("locale").toLocale())); return createReply(returns); } JsonReply* DeviceHandler::GetSupportedDevices(const QVariantMap ¶ms) const { QVariantMap returns; - returns.insert("deviceClasses", JsonTypes::packSupportedDevices(VendorId(params.value("vendorId").toString()))); + returns.insert("deviceClasses", JsonTypes::packSupportedDevices(VendorId(params.value("vendorId").toString()), params.value("locale").toLocale())); return createReply(returns); } @@ -354,7 +355,7 @@ JsonReply* DeviceHandler::GetPlugins(const QVariantMap ¶ms) const Q_UNUSED(params) QVariantMap returns; - returns.insert("plugins", JsonTypes::packPlugins()); + returns.insert("plugins", JsonTypes::packPlugins(params.value("locale").toLocale())); return createReply(returns); } @@ -437,7 +438,7 @@ JsonReply *DeviceHandler::PairDevice(const QVariantMap ¶ms) QVariantMap returns; returns.insert("deviceError", JsonTypes::deviceErrorToString(status)); if (status == DeviceManager::DeviceErrorNoError) { - returns.insert("displayMessage", deviceClass.pairingInfo()); + returns.insert("displayMessage", NymeaCore::instance()->deviceManager()->translator()->translate(deviceClass.pluginId(), deviceClass.pairingInfo(), params.value("locale").toLocale())); returns.insert("pairingTransactionId", pairingTransactionId.toString()); returns.insert("setupMethod", JsonTypes::setupMethod().at(deviceClass.setupMethod())); } @@ -565,7 +566,7 @@ JsonReply* DeviceHandler::GetEventTypes(const QVariantMap ¶ms) const QVariantList eventList; DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(DeviceClassId(params.value("deviceClassId").toString())); foreach (const EventType &eventType, deviceClass.eventTypes()) { - eventList.append(JsonTypes::packEventType(eventType)); + eventList.append(JsonTypes::packEventType(eventType, deviceClass.pluginId(), params.value("locale").toLocale())); } returns.insert("eventTypes", eventList); return createReply(returns); @@ -578,7 +579,7 @@ JsonReply* DeviceHandler::GetActionTypes(const QVariantMap ¶ms) const QVariantList actionList; DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(DeviceClassId(params.value("deviceClassId").toString())); foreach (const ActionType &actionType, deviceClass.actionTypes()) { - actionList.append(JsonTypes::packActionType(actionType)); + actionList.append(JsonTypes::packActionType(actionType, deviceClass.pluginId(), params.value("locale").toLocale())); } returns.insert("actionTypes", actionList); return createReply(returns); @@ -591,7 +592,7 @@ JsonReply* DeviceHandler::GetStateTypes(const QVariantMap ¶ms) const QVariantList stateList; DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(DeviceClassId(params.value("deviceClassId").toString())); foreach (const StateType &stateType, deviceClass.stateTypes()) { - stateList.append(JsonTypes::packStateType(stateType)); + stateList.append(JsonTypes::packStateType(stateType, deviceClass.pluginId(), NymeaCore::instance()->configuration()->locale())); } returns.insert("stateTypes", stateList); return createReply(returns); diff --git a/libnymea-core/jsonrpc/eventhandler.cpp b/libnymea-core/jsonrpc/eventhandler.cpp index c96737be..430e698c 100644 --- a/libnymea-core/jsonrpc/eventhandler.cpp +++ b/libnymea-core/jsonrpc/eventhandler.cpp @@ -88,7 +88,7 @@ JsonReply* EventHandler::GetEventType(const QVariantMap ¶ms) const foreach (const EventType &eventType, deviceClass.eventTypes()) { if (eventType.id() == eventTypeId) { QVariantMap data = statusToReply(DeviceManager::DeviceErrorNoError); - data.insert("eventType", JsonTypes::packEventType(eventType)); + data.insert("eventType", JsonTypes::packEventType(eventType, deviceClass.pluginId(), params.value("locale").toLocale())); return createReply(data); } } diff --git a/libnymea-core/jsonrpc/jsonrpcserver.cpp b/libnymea-core/jsonrpc/jsonrpcserver.cpp index 5aabd58d..016fda76 100644 --- a/libnymea-core/jsonrpc/jsonrpcserver.cpp +++ b/libnymea-core/jsonrpc/jsonrpcserver.cpp @@ -76,6 +76,7 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject params.clear(); returns.clear(); setDescription("Hello", "Upon first connection, nymea will automatically send a welcome message containing information about the setup. If this message is lost for whatever reason (connections with multiple hops might drop this if nymea sends it too early), the exact same message can be retrieved multiple times by calling this Hello method. Note that the contents might change if the system changed its state in the meantime, e.g. initialSetupRequired might turn false if the initial setup has been performed in the meantime."); + params.insert("o:locale", JsonTypes::basicTypeToString(JsonTypes::String)); setParams("Hello", params); returns.insert("id", JsonTypes::basicTypeToString(JsonTypes::Int)); returns.insert("server", JsonTypes::basicTypeToString(JsonTypes::String)); @@ -225,10 +226,19 @@ QString JsonRPCServer::name() const return QStringLiteral("JSONRPC"); } -JsonReply *JsonRPCServer::Hello(const QVariantMap ¶ms) const +JsonReply *JsonRPCServer::Hello(const QVariantMap ¶ms) { Q_UNUSED(params); TransportInterface *interface = reinterpret_cast(property("transportInterface").toLongLong()); + + qCDebug(dcJsonRpc()) << params; + QUuid clientId = this->property("clientId").toUuid(); + if (params.contains("locale")) { + m_clientLocales.insert(clientId, QLocale(params.value("locale").toString())); + } + + qCDebug(dcJsonRpc()) << "Client" << clientId << "initiated handshake." << m_clientLocales.value(clientId); + return createReply(createWelcomeMessage(interface)); } @@ -601,6 +611,17 @@ void JsonRPCServer::processJsonPacket(TransportInterface *interface, const QUuid qCDebug(dcJsonRpc()) << "Invoking method" << targetNamespace << method.toLatin1().data(); + if (targetNamespace != "JSONRPC" || method != "Hello") { + // If the client did request a locale in the Hello message, use that locale + if (m_clientLocales.contains(clientId)) { + params.insert("locale", m_clientLocales.value(clientId)); + } + // Otherwise fall back to the locale set in the configuration. + else { + params.insert("locale", NymeaCore::instance()->configuration()->locale()); + } + } + JsonReply *reply; QMetaObject::invokeMethod(handler, method.toLatin1().data(), Q_RETURN_ARG(JsonReply*, reply), Q_ARG(QVariantMap, params)); if (reply->type() == JsonReply::TypeAsync) { @@ -745,6 +766,7 @@ void JsonRPCServer::clientDisconnected(const QUuid &clientId) m_clientTransports.remove(clientId); m_clientNotifications.remove(clientId); m_clientBuffers.remove(clientId); + m_clientLocales.remove(clientId); if (m_pushButtonTransactions.values().contains(clientId)) { NymeaCore::instance()->userManager()->cancelPushButtonAuth(m_pushButtonTransactions.key(clientId)); } diff --git a/libnymea-core/jsonrpc/jsonrpcserver.h b/libnymea-core/jsonrpc/jsonrpcserver.h index 984712e4..ff246d55 100644 --- a/libnymea-core/jsonrpc/jsonrpcserver.h +++ b/libnymea-core/jsonrpc/jsonrpcserver.h @@ -47,7 +47,7 @@ public: // JsonHandler API implementation QString name() const; - Q_INVOKABLE JsonReply *Hello(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply *Hello(const QVariantMap ¶ms); Q_INVOKABLE JsonReply *Introspect(const QVariantMap ¶ms) const; Q_INVOKABLE JsonReply *Version(const QVariantMap ¶ms) const; Q_INVOKABLE JsonReply *SetNotificationStatus(const QVariantMap ¶ms); @@ -105,6 +105,7 @@ private: QHash m_clientTransports; QHash m_clientBuffers; QHash m_clientNotifications; + QHash m_clientLocales; QHash m_pushButtonTransactions; QHash m_pairingRequests; diff --git a/libnymea-core/jsonrpc/jsontypes.cpp b/libnymea-core/jsonrpc/jsontypes.cpp index 2e82a0e8..4b6e66ad 100644 --- a/libnymea-core/jsonrpc/jsontypes.cpp +++ b/libnymea-core/jsonrpc/jsontypes.cpp @@ -56,6 +56,8 @@ #include "ruleengine.h" #include "loggingcategories.h" #include "logging/logvaluetool.h" +#include "translator.h" +#include "plugin/deviceplugin.h" #include #include @@ -498,12 +500,12 @@ QVariantMap JsonTypes::allTypes() } /*! Returns a variant map of the given \a eventType. */ -QVariantMap JsonTypes::packEventType(const EventType &eventType) +QVariantMap JsonTypes::packEventType(const EventType &eventType, const PluginId &pluginId, const QLocale &locale) { QVariantMap variant; variant.insert("id", eventType.id().toString()); variant.insert("name", eventType.name()); - variant.insert("displayName", eventType.displayName()); + variant.insert("displayName", NymeaCore::instance()->deviceManager()->translator()->translate(pluginId, eventType.displayName(), locale)); variant.insert("index", eventType.index()); if (!eventType.ruleRelevant()) variant.insert("ruleRelevant", false); @@ -513,7 +515,7 @@ QVariantMap JsonTypes::packEventType(const EventType &eventType) QVariantList paramTypes; foreach (const ParamType ¶mType, eventType.paramTypes()) - paramTypes.append(packParamType(paramType)); + paramTypes.append(packParamType(paramType, pluginId, locale)); variant.insert("paramTypes", paramTypes); return variant; @@ -553,16 +555,16 @@ QVariantMap JsonTypes::packEventDescriptor(const EventDescriptor &eventDescripto } /*! Returns a variant map of the given \a actionType. */ -QVariantMap JsonTypes::packActionType(const ActionType &actionType) +QVariantMap JsonTypes::packActionType(const ActionType &actionType, const PluginId &pluginId, const QLocale &locale) { QVariantMap variantMap; variantMap.insert("id", actionType.id().toString()); variantMap.insert("name", actionType.name()); - variantMap.insert("displayName", actionType.displayName()); + variantMap.insert("displayName", NymeaCore::instance()->deviceManager()->translator()->translate(pluginId, actionType.displayName(), locale)); variantMap.insert("index", actionType.index()); QVariantList paramTypes; foreach (const ParamType ¶mType, actionType.paramTypes()) - paramTypes.append(packParamType(paramType)); + paramTypes.append(packParamType(paramType, pluginId, locale)); variantMap.insert("paramTypes", paramTypes); return variantMap; @@ -630,12 +632,12 @@ QVariantMap JsonTypes::packState(const State &state) } /*! Returns a variant map of the given \a stateType. */ -QVariantMap JsonTypes::packStateType(const StateType &stateType) +QVariantMap JsonTypes::packStateType(const StateType &stateType, const PluginId &pluginId, const QLocale &locale) { QVariantMap variantMap; variantMap.insert("id", stateType.id().toString()); variantMap.insert("name", stateType.name()); - variantMap.insert("displayName", stateType.displayName()); + variantMap.insert("displayName", NymeaCore::instance()->deviceManager()->translator()->translate(pluginId, stateType.displayName(), locale)); variantMap.insert("index", stateType.index()); variantMap.insert("type", basicTypeToString(stateType.type())); variantMap.insert("defaultValue", stateType.defaultValue()); @@ -721,12 +723,12 @@ QVariantMap JsonTypes::packParamDescriptor(const ParamDescriptor ¶mDescripto } /*! Returns a variant map of the given \a paramType. */ -QVariantMap JsonTypes::packParamType(const ParamType ¶mType) +QVariantMap JsonTypes::packParamType(const ParamType ¶mType, const PluginId &pluginId, const QLocale &locale) { QVariantMap variantMap; variantMap.insert("id", paramType.id().toString()); variantMap.insert("name", paramType.name()); - variantMap.insert("displayName", paramType.displayName()); + variantMap.insert("displayName", NymeaCore::instance()->deviceManager()->translator()->translate(pluginId, paramType.displayName(), locale)); variantMap.insert("type", basicTypeToString(paramType.type())); variantMap.insert("index", paramType.index()); @@ -756,22 +758,28 @@ QVariantMap JsonTypes::packParamType(const ParamType ¶mType) } /*! Returns a variant map of the given \a vendor. */ -QVariantMap JsonTypes::packVendor(const Vendor &vendor) +QVariantMap JsonTypes::packVendor(const Vendor &vendor, const QLocale &locale) { + DevicePlugin *plugin = nullptr; + foreach (DevicePlugin *p, NymeaCore::instance()->deviceManager()->plugins()) { + if (p->supportedVendors().contains(vendor)) { + plugin = p; + } + } QVariantMap variantMap; variantMap.insert("id", vendor.id().toString()); variantMap.insert("name", vendor.name()); - variantMap.insert("displayName", vendor.displayName()); + variantMap.insert("displayName", NymeaCore::instance()->deviceManager()->translator()->translate(plugin->pluginId(), vendor.displayName(), locale)); return variantMap; } /*! Returns a variant map of the given \a deviceClass. */ -QVariantMap JsonTypes::packDeviceClass(const DeviceClass &deviceClass) +QVariantMap JsonTypes::packDeviceClass(const DeviceClass &deviceClass, const QLocale &locale) { QVariantMap variant; variant.insert("id", deviceClass.id().toString()); variant.insert("name", deviceClass.name()); - variant.insert("displayName", deviceClass.displayName()); + variant.insert("displayName", NymeaCore::instance()->deviceManager()->translator()->translate(deviceClass.pluginId(), deviceClass.displayName(), locale)); variant.insert("vendorId", deviceClass.vendorId().toString()); variant.insert("pluginId", deviceClass.pluginId().toString()); variant.insert("deviceIcon", s_deviceIcon.at(deviceClass.deviceIcon())); @@ -783,23 +791,23 @@ QVariantMap JsonTypes::packDeviceClass(const DeviceClass &deviceClass) QVariantList stateTypes; foreach (const StateType &stateType, deviceClass.stateTypes()) - stateTypes.append(packStateType(stateType)); + stateTypes.append(packStateType(stateType, deviceClass.pluginId(), locale)); QVariantList eventTypes; foreach (const EventType &eventType, deviceClass.eventTypes()) - eventTypes.append(packEventType(eventType)); + eventTypes.append(packEventType(eventType, deviceClass.pluginId(), locale)); QVariantList actionTypes; foreach (const ActionType &actionType, deviceClass.actionTypes()) - actionTypes.append(packActionType(actionType)); + actionTypes.append(packActionType(actionType, deviceClass.pluginId(), locale)); QVariantList paramTypes; foreach (const ParamType ¶mType, deviceClass.paramTypes()) - paramTypes.append(packParamType(paramType)); + paramTypes.append(packParamType(paramType, deviceClass.pluginId(), locale)); QVariantList discoveryParamTypes; foreach (const ParamType ¶mType, deviceClass.discoveryParamTypes()) - discoveryParamTypes.append(packParamType(paramType)); + discoveryParamTypes.append(packParamType(paramType, deviceClass.pluginId(), locale)); if (!deviceClass.criticalStateTypeId().isNull()) variant.insert("criticalStateTypeId", deviceClass.criticalStateTypeId().toString()); @@ -822,16 +830,16 @@ QVariantMap JsonTypes::packDeviceClass(const DeviceClass &deviceClass) } /*! Returns a variant map of the given \a plugin. */ -QVariantMap JsonTypes::packPlugin(DevicePlugin *plugin) +QVariantMap JsonTypes::packPlugin(DevicePlugin *plugin, const QLocale &locale) { QVariantMap pluginMap; pluginMap.insert("id", plugin->pluginId().toString()); pluginMap.insert("name", plugin->pluginName()); - pluginMap.insert("displayName", plugin->pluginDisplayName()); + pluginMap.insert("displayName", NymeaCore::instance()->deviceManager()->translator()->translate(plugin->pluginId(), plugin->pluginDisplayName(), locale)); QVariantList params; foreach (const ParamType ¶m, plugin->configurationDescription()) - params.append(packParamType(param)); + params.append(packParamType(param, plugin->pluginId(), locale)); pluginMap.insert("paramTypes", params); return pluginMap; @@ -1130,21 +1138,21 @@ QVariantMap JsonTypes::packWirelessNetworkDevice(WirelessNetworkDevice *networkD } /*! Returns a variant list of the supported vendors. */ -QVariantList JsonTypes::packSupportedVendors() +QVariantList JsonTypes::packSupportedVendors(const QLocale &locale) { QVariantList supportedVendors; foreach (const Vendor &vendor, NymeaCore::instance()->deviceManager()->supportedVendors()) - supportedVendors.append(packVendor(vendor)); + supportedVendors.append(packVendor(vendor, locale)); return supportedVendors; } /*! Returns a variant list of the supported devices with the given \a vendorId. */ -QVariantList JsonTypes::packSupportedDevices(const VendorId &vendorId) +QVariantList JsonTypes::packSupportedDevices(const VendorId &vendorId, const QLocale &locale) { QVariantList supportedDeviceList; foreach (const DeviceClass &deviceClass, NymeaCore::instance()->deviceManager()->supportedDevices(vendorId)) - supportedDeviceList.append(packDeviceClass(deviceClass)); + supportedDeviceList.append(packDeviceClass(deviceClass, locale)); return supportedDeviceList; } @@ -1246,41 +1254,41 @@ QVariantList JsonTypes::packRuleDescriptions(const QList &rules) } /*! Returns a variant list of action types for the given \a deviceClass. */ -QVariantList JsonTypes::packActionTypes(const DeviceClass &deviceClass) +QVariantList JsonTypes::packActionTypes(const DeviceClass &deviceClass, const QLocale &locale) { QVariantList actionTypes; foreach (const ActionType &actionType, deviceClass.actionTypes()) - actionTypes.append(JsonTypes::packActionType(actionType)); + actionTypes.append(JsonTypes::packActionType(actionType, deviceClass.pluginId(), locale)); return actionTypes; } /*! Returns a variant list of state types for the given \a deviceClass. */ -QVariantList JsonTypes::packStateTypes(const DeviceClass &deviceClass) +QVariantList JsonTypes::packStateTypes(const DeviceClass &deviceClass, const QLocale &locale) { QVariantList stateTypes; foreach (const StateType &stateType, deviceClass.stateTypes()) - stateTypes.append(JsonTypes::packStateType(stateType)); + stateTypes.append(JsonTypes::packStateType(stateType, deviceClass.pluginId(), locale)); return stateTypes; } /*! Returns a variant list of event types for the given \a deviceClass. */ -QVariantList JsonTypes::packEventTypes(const DeviceClass &deviceClass) +QVariantList JsonTypes::packEventTypes(const DeviceClass &deviceClass, const QLocale &locale) { QVariantList eventTypes; foreach (const EventType &eventType, deviceClass.eventTypes()) - eventTypes.append(JsonTypes::packEventType(eventType)); + eventTypes.append(JsonTypes::packEventType(eventType, deviceClass.pluginId(), locale)); return eventTypes; } /*! Returns a variant list containing all plugins. */ -QVariantList JsonTypes::packPlugins() +QVariantList JsonTypes::packPlugins(const QLocale &locale) { QVariantList pluginsList; foreach (DevicePlugin *plugin, NymeaCore::instance()->deviceManager()->plugins()) { - QVariantMap pluginMap = packPlugin(plugin); + QVariantMap pluginMap = packPlugin(plugin, locale); pluginsList.append(pluginMap); } return pluginsList; diff --git a/libnymea-core/jsonrpc/jsontypes.h b/libnymea-core/jsonrpc/jsontypes.h index 7cfa785a..dda5c3cb 100644 --- a/libnymea-core/jsonrpc/jsontypes.h +++ b/libnymea-core/jsonrpc/jsontypes.h @@ -180,23 +180,23 @@ public: DECLARE_OBJECT(mqttPolicy, "MqttPolicy") // pack types - static QVariantMap packEventType(const EventType &eventType); + static QVariantMap packEventType(const EventType &eventType, const PluginId &pluginId, const QLocale &locale); static QVariantMap packEvent(const Event &event); static QVariantMap packEventDescriptor(const EventDescriptor &event); - static QVariantMap packActionType(const ActionType &actionType); + static QVariantMap packActionType(const ActionType &actionType, const PluginId &pluginId, const QLocale &locale); static QVariantMap packAction(const Action &action); static QVariantMap packRuleAction(const RuleAction &ruleAction); static QVariantMap packRuleActionParam(const RuleActionParam &ruleActionParam); static QVariantMap packState(const State &state); - static QVariantMap packStateType(const StateType &stateType); + static QVariantMap packStateType(const StateType &stateType, const PluginId &pluginId, const QLocale &locale); static QVariantMap packStateDescriptor(const StateDescriptor &stateDescriptor); static QVariantMap packStateEvaluator(const StateEvaluator &stateEvaluator); static QVariantMap packParam(const Param ¶m); - static QVariantMap packParamType(const ParamType ¶mType); + static QVariantMap packParamType(const ParamType ¶mType, const PluginId &pluginId, const QLocale &locale); static QVariantMap packParamDescriptor(const ParamDescriptor ¶mDescriptor); - static QVariantMap packVendor(const Vendor &vendor); - static QVariantMap packDeviceClass(const DeviceClass &deviceClass); - static QVariantMap packPlugin(DevicePlugin *plugin); + static QVariantMap packVendor(const Vendor &vendor, const QLocale &locale); + static QVariantMap packDeviceClass(const DeviceClass &deviceClass, const QLocale &locale); + static QVariantMap packPlugin(DevicePlugin *pluginid, const QLocale &locale); static QVariantMap packDevice(Device *device); static QVariantMap packDeviceDescriptor(const DeviceDescriptor &descriptor); static QVariantMap packRule(const Rule &rule); @@ -213,8 +213,8 @@ public: static QVariantList packRules(const QList rules); static QVariantList packCreateMethods(DeviceClass::CreateMethods createMethods); - static QVariantList packSupportedVendors(); - static QVariantList packSupportedDevices(const VendorId &vendorId); + static QVariantList packSupportedVendors(const QLocale &locale); + static QVariantList packSupportedDevices(const VendorId &vendorId, const QLocale &locale); static QVariantList packConfiguredDevices(); static QVariantList packDeviceStates(Device *device); static QVariantList packDeviceDescriptors(const QList deviceDescriptors); @@ -227,10 +227,10 @@ public: static QVariantList packRuleDescriptions(); static QVariantList packRuleDescriptions(const QList &rules); - static QVariantList packActionTypes(const DeviceClass &deviceClass); - static QVariantList packStateTypes(const DeviceClass &deviceClass); - static QVariantList packEventTypes(const DeviceClass &deviceClass); - static QVariantList packPlugins(); + static QVariantList packActionTypes(const DeviceClass &deviceClass, const QLocale &locale); + static QVariantList packStateTypes(const DeviceClass &deviceClass, const QLocale &locale); + static QVariantList packEventTypes(const DeviceClass &deviceClass, const QLocale &locale); + static QVariantList packPlugins(const QLocale &locale); static QVariantMap packTokenInfo(const TokenInfo &tokenInfo); diff --git a/libnymea-core/jsonrpc/statehandler.cpp b/libnymea-core/jsonrpc/statehandler.cpp index e70fbff3..4b34641e 100644 --- a/libnymea-core/jsonrpc/statehandler.cpp +++ b/libnymea-core/jsonrpc/statehandler.cpp @@ -68,7 +68,7 @@ JsonReply* StateHandler::GetStateType(const QVariantMap ¶ms) const foreach (const StateType &stateType, deviceClass.stateTypes()) { if (stateType.id() == stateTypeId) { QVariantMap data = statusToReply(DeviceManager::DeviceErrorNoError); - data.insert("stateType", JsonTypes::packStateType(stateType)); + data.insert("stateType", JsonTypes::packStateType(stateType, deviceClass.pluginId(), params.value("locale").toLocale())); return createReply(data); } } diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp index 104b5290..ea9ebff1 100644 --- a/libnymea-core/nymeacore.cpp +++ b/libnymea-core/nymeacore.cpp @@ -118,6 +118,7 @@ #include "cloud/cloudtransport.h" #include +#include namespace nymeaserver { @@ -184,7 +185,6 @@ void NymeaCore::init() { CloudTransport *cloudTransport = m_cloudManager->createTransportInterface(); m_serverManager->jsonServer()->registerTransportInterface(cloudTransport, false); - connect(m_configuration, &NymeaConfiguration::localeChanged, this, &NymeaCore::onLocaleChanged); connect(m_configuration, &NymeaConfiguration::serverNameChanged, m_serverManager, &ServerManager::setServerName); connect(m_deviceManager, &DeviceManager::pluginConfigChanged, this, &NymeaCore::pluginConfigChanged); @@ -533,9 +533,20 @@ QStringList NymeaCore::getAvailableLanguages() { qCDebug(dcApplication()) << "Loading translations from" << NymeaSettings::translationsPath(); - QDir translationDirectory(NymeaSettings::translationsPath()); - translationDirectory.setNameFilters(QStringList() << "*.qm"); - QStringList translationFiles = translationDirectory.entryList(); + QStringList searchPaths; + searchPaths << QCoreApplication::applicationDirPath() + "/../translations"; + searchPaths << NymeaSettings::translationsPath(); + + QStringList translationFiles; + foreach (const QString &path, searchPaths) { + QDir translationDirectory(path); + translationDirectory.setNameFilters(QStringList() << "*.qm"); + translationFiles = translationDirectory.entryList(); + qCDebug(dcTranslations()) << translationFiles.count() << "translations in" << path; + if (translationFiles.count() > 0) { + break; + } + } QStringList availableLanguages; foreach (QString translationFile, translationFiles) { @@ -687,11 +698,6 @@ void NymeaCore::onDateTimeChanged(const QDateTime &dateTime) executeRuleActions(actions); } -void NymeaCore::onLocaleChanged() -{ - m_deviceManager->setLocale(m_configuration->locale()); -} - /*! Return the instance of the log engine */ LogEngine* NymeaCore::logEngine() const { diff --git a/libnymea-core/nymeacore.h b/libnymea-core/nymeacore.h index f3aee522..7911e5d9 100644 --- a/libnymea-core/nymeacore.h +++ b/libnymea-core/nymeacore.h @@ -136,7 +136,6 @@ private: private slots: void gotEvent(const Event &event); void onDateTimeChanged(const QDateTime &dateTime); - void onLocaleChanged(); void actionExecutionFinished(const ActionId &id, DeviceManager::DeviceError status); void onDeviceDisappeared(const DeviceId &deviceId); void deviceManagerLoaded(); diff --git a/libnymea-core/servers/rest/deviceclassesresource.cpp b/libnymea-core/servers/rest/deviceclassesresource.cpp index 02a0edb1..586edfab 100644 --- a/libnymea-core/servers/rest/deviceclassesresource.cpp +++ b/libnymea-core/servers/rest/deviceclassesresource.cpp @@ -184,7 +184,7 @@ HttpReply *DeviceClassesResource::getDeviceClass() qCDebug(dcRest) << "Get device class with id " << m_deviceClass.id(); HttpReply *reply = createSuccessReply(); reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); - reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packDeviceClass(m_deviceClass)).toJson()); + reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packDeviceClass(m_deviceClass, NymeaCore::instance()->configuration()->locale())).toJson()); return reply; } @@ -193,7 +193,7 @@ HttpReply *DeviceClassesResource::getActionTypes() qCDebug(dcRest) << "Get action types for device class" << m_deviceClass.id(); HttpReply *reply = createSuccessReply(); reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); - reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packActionTypes(m_deviceClass)).toJson()); + reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packActionTypes(m_deviceClass, NymeaCore::instance()->configuration()->locale())).toJson()); return reply; } @@ -205,7 +205,7 @@ HttpReply *DeviceClassesResource::getActionType(const ActionTypeId &actionTypeId if (actionType.id() == actionTypeId) { HttpReply *reply = createSuccessReply(); reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); - reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packActionType(actionType)).toJson()); + reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packActionType(actionType, m_deviceClass.pluginId(), NymeaCore::instance()->configuration()->locale())).toJson()); return reply; } } @@ -217,7 +217,7 @@ HttpReply *DeviceClassesResource::getStateTypes() qCDebug(dcRest) << "Get state types for device class" << m_deviceClass.id(); HttpReply *reply = createSuccessReply(); reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); - reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packStateTypes(m_deviceClass)).toJson()); + reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packStateTypes(m_deviceClass, NymeaCore::instance()->configuration()->locale())).toJson()); return reply; } @@ -229,7 +229,7 @@ HttpReply *DeviceClassesResource::getStateType(const StateTypeId &stateTypeId) if (stateType.id() == stateTypeId) { HttpReply *reply = createSuccessReply(); reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); - reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packStateType(stateType)).toJson()); + reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packStateType(stateType, m_deviceClass.pluginId(), NymeaCore::instance()->configuration()->locale())).toJson()); return reply; } } @@ -241,7 +241,7 @@ HttpReply *DeviceClassesResource::getEventTypes() qCDebug(dcRest) << "Get event types for device class" << m_deviceClass.id(); HttpReply *reply = createSuccessReply(); reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); - reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packEventTypes(m_deviceClass)).toJson()); + reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packEventTypes(m_deviceClass, NymeaCore::instance()->configuration()->locale())).toJson()); return reply; } @@ -253,7 +253,7 @@ HttpReply *DeviceClassesResource::getEventType(const EventTypeId &eventTypeId) if (eventType.id() == eventTypeId) { HttpReply *reply = createSuccessReply(); reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); - reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packEventType(eventType)).toJson()); + reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packEventType(eventType, m_deviceClass.pluginId(), NymeaCore::instance()->configuration()->locale())).toJson()); return reply; } } @@ -307,7 +307,7 @@ HttpReply *DeviceClassesResource::getDeviceClasses(const VendorId &vendorId) HttpReply *reply = createSuccessReply(); reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); - reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packSupportedDevices(vendorId)).toJson()); + reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packSupportedDevices(vendorId, NymeaCore::instance()->configuration()->locale())).toJson()); return reply; } diff --git a/libnymea-core/servers/rest/deviceclassesresource.h b/libnymea-core/servers/rest/deviceclassesresource.h index 96e94338..aba8fb86 100644 --- a/libnymea-core/servers/rest/deviceclassesresource.h +++ b/libnymea-core/servers/rest/deviceclassesresource.h @@ -37,7 +37,7 @@ class DeviceClassesResource : public RestResource { Q_OBJECT public: - explicit DeviceClassesResource(QObject *parent = 0); + explicit DeviceClassesResource(QObject *parent = nullptr); QString name() const override; diff --git a/libnymea-core/servers/rest/devicesresource.cpp b/libnymea-core/servers/rest/devicesresource.cpp index 68693cde..069db826 100644 --- a/libnymea-core/servers/rest/devicesresource.cpp +++ b/libnymea-core/servers/rest/devicesresource.cpp @@ -41,6 +41,7 @@ #include "servers/httprequest.h" #include "jsonrpc/jsontypes.h" #include "nymeacore.h" +#include "translator.h" #include @@ -442,7 +443,7 @@ HttpReply *DevicesResource::pairDevice(const QByteArray &payload) const return createDeviceErrorReply(HttpReply::BadRequest, status); QVariantMap returns; - returns.insert("displayMessage", deviceClass.pairingInfo()); + returns.insert("displayMessage", NymeaCore::instance()->deviceManager()->translator()->translate(deviceClass.pluginId(), deviceClass.pairingInfo(), NymeaCore::instance()->configuration()->locale())); returns.insert("pairingTransactionId", pairingTransactionId.toString()); returns.insert("setupMethod", JsonTypes::setupMethod().at(deviceClass.setupMethod())); HttpReply *reply = createSuccessReply(); diff --git a/libnymea-core/servers/rest/pluginsresource.cpp b/libnymea-core/servers/rest/pluginsresource.cpp index dd0a10bd..66b16257 100644 --- a/libnymea-core/servers/rest/pluginsresource.cpp +++ b/libnymea-core/servers/rest/pluginsresource.cpp @@ -139,7 +139,7 @@ HttpReply *PluginsResource::getPlugins() const qCDebug(dcRest) << "Get plugins"; HttpReply *reply = createSuccessReply(); reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); - reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packPlugins()).toJson()); + reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packPlugins(NymeaCore::instance()->configuration()->locale())).toJson()); return reply; } @@ -150,7 +150,7 @@ HttpReply *PluginsResource::getPlugin(const PluginId &pluginId) const foreach (DevicePlugin *plugin, NymeaCore::instance()->deviceManager()->plugins()) { if (plugin->pluginId() == pluginId) { reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); - reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packPlugin(plugin)).toJson()); + reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packPlugin(plugin, NymeaCore::instance()->configuration()->locale())).toJson()); return reply; } } diff --git a/libnymea-core/servers/rest/vendorsresource.cpp b/libnymea-core/servers/rest/vendorsresource.cpp index e8c2058f..2194be13 100644 --- a/libnymea-core/servers/rest/vendorsresource.cpp +++ b/libnymea-core/servers/rest/vendorsresource.cpp @@ -111,7 +111,7 @@ HttpReply *VendorsResource::getVendors() const QVariantList vendorsList; foreach (const Vendor &vendor, NymeaCore::instance()->deviceManager()->supportedVendors()) { - vendorsList.append(JsonTypes::packVendor(vendor)); + vendorsList.append(JsonTypes::packVendor(vendor, NymeaCore::instance()->configuration()->locale())); } reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); reply->setPayload(QJsonDocument::fromVariant(vendorsList).toJson()); @@ -125,7 +125,7 @@ HttpReply *VendorsResource::getVendor(const VendorId &vendorId) const if (vendor.id() == vendorId) { HttpReply *reply = createSuccessReply(); reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";"); - reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packVendor(vendor)).toJson()); + reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packVendor(vendor, NymeaCore::instance()->configuration()->locale())).toJson()); return reply; } } diff --git a/libnymea/devicemanager.cpp b/libnymea/devicemanager.cpp index c7e2be1b..be059f81 100644 --- a/libnymea/devicemanager.cpp +++ b/libnymea/devicemanager.cpp @@ -181,6 +181,7 @@ #include "typeutils.h" #include "nymeasettings.h" #include "unistd.h" +#include "translator.h" #include "plugintimer.h" @@ -198,7 +199,8 @@ DeviceManager::DeviceManager(HardwareManager *hardwareManager, const QLocale &locale, QObject *parent) : QObject(parent), m_hardwareManager(hardwareManager), - m_locale(locale) + m_locale(locale), + m_translator(new Translator(this)) { qRegisterMetaType(); qRegisterMetaType(); @@ -219,6 +221,8 @@ DeviceManager::DeviceManager(HardwareManager *hardwareManager, const QLocale &lo /*! Destructor of the DeviceManager. Each loaded \l{DevicePlugin} will be deleted. */ DeviceManager::~DeviceManager() { + delete m_translator; + foreach (Device *device, m_configuredDevices) { storeDeviceStates(device); } @@ -288,44 +292,6 @@ void DeviceManager::registerStaticPlugin(DevicePlugin *plugin, const QJsonObject loadPlugin(plugin); } -/*! Set the \a locale of all plugins and reload the translated strings. */ -void DeviceManager::setLocale(const QLocale &locale) -{ - qCDebug(dcDeviceManager()) << "Setting locale:" << locale; - m_locale = locale; - foreach (DevicePlugin *plugin, m_devicePlugins.values()) { - QCoreApplication::removeTranslator(plugin->translator()); - plugin->setLocale(m_locale); - QCoreApplication::installTranslator(plugin->translator()); - plugin->loadMetaData(); - } - - // Reload all plugin meta data - - m_supportedVendors.clear(); - m_supportedDevices.clear(); - - foreach (DevicePlugin *plugin, m_devicePlugins.values()) { - - foreach (const Vendor &vendor, plugin->supportedVendors()) { - if (m_supportedVendors.contains(vendor.id())) - continue; - - m_supportedVendors.insert(vendor.id(), vendor); - } - - foreach (const DeviceClass &deviceClass, plugin->supportedDevices()) { - if (!m_supportedVendors.contains(deviceClass.vendorId())) { - qCWarning(dcDeviceManager) << "Vendor not found. Ignoring device. VendorId:" << deviceClass.vendorId() << "DeviceClass:" << deviceClass.name() << deviceClass.id(); - continue; - } - m_supportedDevices.insert(deviceClass.id(), deviceClass); - } - } - - emit languageUpdated(); -} - /*! Returns the pointer to the \l{HardwareManager} of the system. \sa HardwareManager @@ -400,13 +366,15 @@ Interface DeviceManager::findInterface(const QString &name) * Optionally filtered by \a vendorId. */ QList DeviceManager::supportedDevices(const VendorId &vendorId) const { - QList ret; if (vendorId.isNull()) { - ret = m_supportedDevices.values(); - } else { - foreach (const DeviceClassId &deviceClassId, m_vendorDeviceMap.value(vendorId)) { - ret.append(m_supportedDevices.value(deviceClassId)); + return m_supportedDevices.values(); + } + QList ret; + foreach (const DeviceClass &deviceClass, m_supportedDevices) { + if (!vendorId.isNull() && deviceClass.vendorId() != vendorId) { + continue; } + ret.append(deviceClass); } return ret; } @@ -928,13 +896,13 @@ DeviceManager::DeviceError DeviceManager::verifyParam(const ParamType ¶mType return DeviceErrorInvalidParameter; } - if (!param.value().canConvert(paramType.type())) { - qCWarning(dcDeviceManager) << "Wrong parameter type for param" << param.paramTypeId().toString() << " Got:" << param.value() << " Expected:" << QVariant::typeToName(paramType.type()); + if (!param.value().canConvert(static_cast(paramType.type()))) { + qCWarning(dcDeviceManager) << "Wrong parameter type for param" << param.paramTypeId().toString() << " Got:" << param.value() << " Expected:" << QVariant::typeToName(static_cast(paramType.type())); return DeviceErrorInvalidParameter; } - if (!param.value().convert(paramType.type())) { - qCWarning(dcDeviceManager) << "Could not convert value of param" << param.paramTypeId().toString() << " to:" << QVariant::typeToName(paramType.type()) << " Got:" << param.value(); + if (!param.value().convert(static_cast(paramType.type()))) { + qCWarning(dcDeviceManager) << "Could not convert value of param" << param.paramTypeId().toString() << " to:" << QVariant::typeToName(static_cast(paramType.type())) << " Got:" << param.value(); return DeviceErrorInvalidParameter; } @@ -993,6 +961,13 @@ DeviceManager::DeviceError DeviceManager::verifyParam(const ParamType ¶mType return DeviceErrorNoError; } +/*! Returns the translator. The translator can be used to translate plugin data. + * */ +Translator *DeviceManager::translator() const +{ + return m_translator; +} + /*! Execute the given \l{Action}. * This will find the \l{Device} \a action refers to the \l{Action}{deviceId()} and * its \l{DevicePlugin}. Then will dispatch the execution to the \l{DevicePlugin}.*/ @@ -1079,9 +1054,6 @@ void DeviceManager::loadPlugins() void DeviceManager::loadPlugin(DevicePlugin *pluginIface) { - pluginIface->setLocale(m_locale); - qApp->installTranslator(pluginIface->translator()); - pluginIface->initPlugin(this); qCDebug(dcDeviceManager) << "**** Loaded plugin" << pluginIface->pluginName(); @@ -1141,6 +1113,7 @@ void DeviceManager::loadPlugin(DevicePlugin *pluginIface) connect(pluginIface, &DevicePlugin::pairingFinished, this, &DeviceManager::slotPairingFinished); connect(pluginIface, &DevicePlugin::autoDevicesAppeared, this, &DeviceManager::onAutoDevicesAppeared); connect(pluginIface, &DevicePlugin::autoDeviceDisappeared, this, &DeviceManager::onAutoDeviceDisappeared); + } void DeviceManager::loadConfiguredDevices() diff --git a/libnymea/devicemanager.h b/libnymea/devicemanager.h index 9d542887..394c9b12 100644 --- a/libnymea/devicemanager.h +++ b/libnymea/devicemanager.h @@ -39,6 +39,7 @@ #include #include #include +#include #include "hardwaremanager.h" @@ -46,6 +47,7 @@ class Device; class DevicePlugin; class DevicePairingInfo; class HardwareManager; +class Translator; class LIBNYMEA_EXPORT DeviceManager : public QObject { @@ -96,8 +98,6 @@ public: static QList pluginsMetadata(); void registerStaticPlugin(DevicePlugin* plugin, const QJsonObject &metaData); - void setLocale(const QLocale &locale); - HardwareManager *hardwareManager() const; QList plugins() const; @@ -136,9 +136,9 @@ public: DeviceError verifyParam(const QList paramTypes, const Param ¶m); DeviceError verifyParam(const ParamType ¶mType, const Param ¶m); + Translator* translator() const; signals: void loaded(); - void languageUpdated(); void pluginConfigChanged(const PluginId &id, const ParamList &config); void eventTriggered(const Event &event); void deviceStateChanged(Device *device, const QUuid &stateTypeId, const QVariant &value); @@ -186,6 +186,7 @@ private: HardwareManager *m_hardwareManager; QLocale m_locale; + Translator *m_translator = nullptr; QHash m_supportedVendors; QHash m_supportedInterfaces; QHash > m_vendorDeviceMap; diff --git a/libnymea/libnymea.pro b/libnymea/libnymea.pro index 103901b9..2e1b544b 100644 --- a/libnymea/libnymea.pro +++ b/libnymea/libnymea.pro @@ -73,7 +73,8 @@ HEADERS += devicemanager.h \ hardwaremanager.h \ nymeadbusservice.h \ network/mqtt/mqttprovider.h \ - network/mqtt/mqttchannel.h + network/mqtt/mqttchannel.h \ + translator.h SOURCES += devicemanager.cpp \ loggingcategories.cpp \ @@ -132,7 +133,8 @@ SOURCES += devicemanager.cpp \ hardwaremanager.cpp \ nymeadbusservice.cpp \ network/mqtt/mqttprovider.cpp \ - network/mqtt/mqttchannel.cpp + network/mqtt/mqttchannel.cpp \ + translator.cpp # install plugininfo python script for libnymea-dev diff --git a/libnymea/loggingcategories.cpp b/libnymea/loggingcategories.cpp index d93ddb5e..a2eba450 100644 --- a/libnymea/loggingcategories.cpp +++ b/libnymea/loggingcategories.cpp @@ -53,3 +53,4 @@ Q_LOGGING_CATEGORY(dcAWSTraffic, "AWSTraffic") Q_LOGGING_CATEGORY(dcBluetoothServer, "BluetoothServer") Q_LOGGING_CATEGORY(dcBluetoothServerTraffic, "BluetoothServerTraffic") Q_LOGGING_CATEGORY(dcMqtt, "Mqtt") +Q_LOGGING_CATEGORY(dcTranslations, "Translations") diff --git a/libnymea/loggingcategories.h b/libnymea/loggingcategories.h index 4d1a8d21..809241dd 100644 --- a/libnymea/loggingcategories.h +++ b/libnymea/loggingcategories.h @@ -61,5 +61,6 @@ Q_DECLARE_LOGGING_CATEGORY(dcAWSTraffic) Q_DECLARE_LOGGING_CATEGORY(dcBluetoothServer) Q_DECLARE_LOGGING_CATEGORY(dcBluetoothServerTraffic) Q_DECLARE_LOGGING_CATEGORY(dcMqtt) +Q_DECLARE_LOGGING_CATEGORY(dcTranslations) #endif // LOGGINGCATEGORYS_H diff --git a/libnymea/plugin/deviceplugin.cpp b/libnymea/plugin/deviceplugin.cpp index 2e2fc74c..9a52c49a 100644 --- a/libnymea/plugin/deviceplugin.cpp +++ b/libnymea/plugin/deviceplugin.cpp @@ -104,8 +104,7 @@ /*! DevicePlugin constructor. DevicePlugins will be instantiated by the DeviceManager, its \a parent. */ DevicePlugin::DevicePlugin(QObject *parent): - QObject(parent), - m_translator(new QTranslator(this)) + QObject(parent) { } @@ -124,7 +123,7 @@ QString DevicePlugin::pluginName() const /*! Returns the displayName of this DevicePlugin, to be shown to the user, translated. */ QString DevicePlugin::pluginDisplayName() const { - return translateValue(m_metaData.value("name").toString(), m_metaData.value("displayName").toString()); + return m_metaData.value("displayName").toString(); } /*! Returns the id of this DevicePlugin. @@ -141,7 +140,7 @@ QList DevicePlugin::supportedVendors() const QList vendors; foreach (const QJsonValue &vendorJson, m_metaData.value("vendors").toArray()) { Vendor vendor(vendorJson.toObject().value("id").toString(), vendorJson.toObject().value("name").toString()); - vendor.setDisplayName(translateValue(m_metaData.value("name").toString(), vendorJson.toObject().value("displayName").toString())); + vendor.setDisplayName(vendorJson.toObject().value("displayName").toString()); vendors.append(vendor); } return vendors; @@ -155,33 +154,6 @@ QList DevicePlugin::supportedDevices() const return m_supportedDevices; } -/*! Returns the translator of this \l{DevicePlugin}. */ -QTranslator *DevicePlugin::translator() -{ - return m_translator; -} - -/*! Returns true if the given \a locale could be set for this \l{DevicePlugin}. */ -bool DevicePlugin::setLocale(const QLocale &locale) -{ - // check if there are local translations - if (m_translator->load(locale, m_metaData.value("id").toString(), "-", QDir(QCoreApplication::applicationDirPath() + "../../translations/").absolutePath(), ".qm")) { - qCDebug(dcDeviceManager()) << "* Load translation" << locale.name() << "for" << pluginName() << "from" << QDir(QCoreApplication::applicationDirPath() + "../../translations/").absolutePath() + "/" + m_metaData.value("id").toString() + "-" + locale.name() + ".qm"; - return true; - } - - // otherwise use the system translations - if (m_translator->load(locale, m_metaData.value("id").toString(), "-", NymeaSettings::translationsPath(), ".qm")) { - qCDebug(dcDeviceManager()) << "* Load translation" << locale.name() << "for" << pluginName() << "from" << NymeaSettings::translationsPath(); - return true; - } - - if (locale.name() != "en_US") - qCWarning(dcDeviceManager()) << "* Could not load translation" << locale.name() << "for plugin" << pluginName(); - - return false; -} - /*! Override this if your plugin supports Device with DeviceClass::CreationMethodAuto. This will be called at startup, after the configured devices have been loaded. This is the earliest time you should start emitting autoDevicesAppeared(). If you @@ -327,7 +299,7 @@ QPair > DevicePlugin::parseParamTypes(const QJsonArray &a } ParamType paramType(ParamTypeId(pt.value("id").toString()), pt.value("name").toString(), t, pt.value("defaultValue").toVariant()); - paramType.setDisplayName(translateValue(m_metaData.value("name").toString(), pt.value("displayName").toString())); + paramType.setDisplayName(pt.value("displayName").toString()); // Set allowed values @@ -593,7 +565,7 @@ void DevicePlugin::loadMetaData() DeviceClass deviceClass(pluginId(), vendorId, deviceClassObject.value("id").toString()); deviceClass.setName(deviceClassObject.value("name").toString()); - deviceClass.setDisplayName(translateValue(m_metaData.value("name").toString(), deviceClassObject.value("displayName").toString())); + deviceClass.setDisplayName(deviceClassObject.value("displayName").toString()); // Read create methods DeviceClass::CreateMethods createMethods; @@ -665,7 +637,7 @@ void DevicePlugin::loadMetaData() deviceClass.setSetupMethod(setupMethod); // Read pairing info - deviceClass.setPairingInfo(translateValue(m_metaData.value("name").toString(), deviceClassObject.value("pairingInfo").toString())); + deviceClass.setPairingInfo(deviceClassObject.value("pairingInfo").toString()); // Read basic tags QList basicTags; @@ -725,7 +697,7 @@ void DevicePlugin::loadMetaData() StateType stateType(st.value("id").toString()); stateType.setName(st.value("name").toString()); - stateType.setDisplayName(translateValue(m_metaData.value("name").toString(), st.value("displayName").toString())); + stateType.setDisplayName(st.value("displayName").toString()); stateType.setIndex(index++); stateType.setType(t); QPair unitVerification = loadAndVerifyUnit(st.value("unit").toString()); @@ -775,9 +747,9 @@ void DevicePlugin::loadMetaData() eventType.setRuleRelevant(st.value("eventRuleRelevant").toBool()); eventType.setName(st.value("name").toString()); - eventType.setDisplayName(translateValue(m_metaData.value("name").toString(), st.value("displayNameEvent").toString())); + eventType.setDisplayName(st.value("displayNameEvent").toString()); ParamType paramType(ParamTypeId(stateType.id().toString()), st.value("name").toString(), stateType.type()); - paramType.setDisplayName(translateValue(m_metaData.value("name").toString(), st.value("displayName").toString())); + paramType.setDisplayName(st.value("displayName").toString()); paramType.setAllowedValues(stateType.possibleValues()); paramType.setDefaultValue(stateType.defaultValue()); paramType.setMinValue(stateType.minValue()); @@ -791,7 +763,7 @@ void DevicePlugin::loadMetaData() if (writableState) { ActionType actionType(ActionTypeId(stateType.id().toString())); actionType.setName(stateType.name()); - actionType.setDisplayName(translateValue(m_metaData.value("name").toString(), st.value("displayNameAction").toString())); + actionType.setDisplayName(st.value("displayNameAction").toString()); actionType.setIndex(stateType.index()); actionType.setParamTypes(QList() << paramType); actionTypes.append(actionType); @@ -821,7 +793,7 @@ void DevicePlugin::loadMetaData() ActionType actionType(at.value("id").toString()); actionType.setName(at.value("name").toString()); - actionType.setDisplayName(translateValue(m_metaData.value("name").toString(), at.value("displayName").toString())); + actionType.setDisplayName(at.value("displayName").toString()); actionType.setIndex(index++); QPair > paramVerification = parseParamTypes(at.value("paramTypes").toArray()); @@ -859,7 +831,7 @@ void DevicePlugin::loadMetaData() EventType eventType(et.value("id").toString()); eventType.setName(et.value("name").toString()); - eventType.setDisplayName(translateValue(m_metaData.value("name").toString(), et.value("displayName").toString())); + eventType.setDisplayName(et.value("displayName").toString()); eventType.setIndex(index++); if (et.contains("ruleRelevant")) eventType.setRuleRelevant(et.value("ruleRelevant").toBool()); @@ -1024,15 +996,6 @@ void DevicePlugin::loadMetaData() } } -QString DevicePlugin::translateValue(const QString &context, const QString &string) const -{ - QString translation = m_translator->translate(context.toUtf8().constData(), string.toUtf8().constData()); - if (translation.isEmpty()) - translation = string; - - return translation; -} - QPair DevicePlugin::loadAndVerifyUnit(const QString &unitString) const { if (unitString.isEmpty()) diff --git a/libnymea/plugin/deviceplugin.h b/libnymea/plugin/deviceplugin.h index 5bdeacbd..f642374a 100644 --- a/libnymea/plugin/deviceplugin.h +++ b/libnymea/plugin/deviceplugin.h @@ -63,9 +63,6 @@ public: QList supportedVendors() const; QList supportedDevices() const; - QTranslator *translator(); - bool setLocale(const QLocale &locale); - virtual void startMonitoringAutoDevices(); virtual DeviceManager::DeviceError discoverDevices(const DeviceClassId &deviceClassId, const ParamList ¶ms); @@ -111,8 +108,6 @@ private: // Returns QPair verifyFields(const QStringList &possibleFields, const QStringList &mandatoryFields, const QJsonObject &value) const; - QString translateValue(const QString &context, const QString &string) const; - // load and verify enum values QPair loadAndVerifyUnit(const QString &unitString) const; QPair loadAndVerifyInputType(const QString &inputType) const; @@ -127,7 +122,6 @@ private: static Interface mergeInterfaces(const Interface &iface1, const Interface &iface2); static QStringList generateInterfaceParentList(const QString &interface); - QTranslator *m_translator = nullptr; DeviceManager *m_deviceManager = nullptr; QList m_configurationDescription; diff --git a/libnymea/translator.cpp b/libnymea/translator.cpp new file mode 100644 index 00000000..4c678c52 --- /dev/null +++ b/libnymea/translator.cpp @@ -0,0 +1,183 @@ +#include "translator.h" +#include "nymeasettings.h" + +#include "loggingcategories.h" +#include "plugin/deviceplugin.h" +#include +#include + +Translator::Translator(DeviceManager *deviceManager): + m_deviceManager(deviceManager) +{ + +} + +Translator::~Translator() +{ + foreach (const TranslatorContext &ctx, m_translatorContexts) { + foreach (QTranslator *t, ctx.translators) { + t->deleteLater(); + } + } + m_translatorContexts.clear(); +} + +//DeviceClass Translator::translate(DevicePlugin* plugin, const DeviceClass &deviceClass, const QLocale &locale) +//{ +// if (!m_translatorContexts.contains(deviceClass.pluginId()) || !m_translatorContexts.value(deviceClass.pluginId()).translators.contains(locale)) { +// loadTranslator(plugin, locale); +// } + +// QTranslator* translator = m_translatorContexts.value(deviceClass.pluginId()).translators.value(locale); + +// DeviceClass translatedDeviceClass(deviceClass); +// QString translatedDisplayName = translator->translate(plugin->pluginName().toUtf8(), deviceClass.displayName().toUtf8()); +// if (!translatedDisplayName.isEmpty()) { +// translatedDeviceClass.setDisplayName(translatedDisplayName); +// } + +// // ParamTypes +// ParamTypes translatedParamTypes; +// foreach (ParamType paramType, deviceClass.paramTypes()) { +// QString translated = translator->translate(plugin->pluginName().toUtf8(), paramType.displayName().toUtf8()); +// if (!translated.isEmpty()) { +// paramType.setDisplayName(translated); +// } +// translatedParamTypes.append(paramType); +// } +// translatedDeviceClass.setParamTypes(translatedParamTypes); + +// // DiscoveryParamTypes +// ParamTypes translatedDiscoveryParamTypes; +// foreach (ParamType discoveryParamType, deviceClass.discoveryParamTypes()) { +// QString translated = translator->translate(plugin->pluginName().toUtf8(), discoveryParamType.displayName().toUtf8()); +// if (!translated.isEmpty()) { +// discoveryParamType.setDisplayName(translated); +// } +// translatedParamTypes.append(discoveryParamType); +// } +// translatedDeviceClass.setDiscoveryParamTypes(translatedDiscoveryParamTypes); + +// // EventTypes +// EventTypes translatedEventTypes; +// foreach (EventType eventType, deviceClass.eventTypes()) { +// QString translated = translator->translate(plugin->pluginName().toUtf8(), eventType.displayName().toUtf8()); +// if (!translated.isEmpty()) { +// eventType.setDisplayName(translated); +// } +// ParamTypes translatedEventParamTypes; +// foreach (ParamType paramType, eventType.paramTypes()) { +// translated = translator->translate(plugin->pluginName().toUtf8(), paramType.displayName().toUtf8()); +// if (!translated.isEmpty()) { +// paramType.setDisplayName(translated); +// } +// translatedEventParamTypes.append(paramType); +// } +// eventType.setParamTypes(translatedEventParamTypes); +// translatedEventTypes.append(eventType); +// } +// translatedDeviceClass.setEventTypes(translatedEventTypes); + +// // StateTypes +// StateTypes translatedStateTypes; +// foreach (StateType stateType, deviceClass.stateTypes()) { +// QString translated = translator->translate(plugin->pluginName().toUtf8(), stateType.displayName().toUtf8()); +// if (!translated.isEmpty()) { +// stateType.setDisplayName(translated); +// } +// translatedStateTypes.append(stateType); +// } +// translatedDeviceClass.setStateTypes(translatedStateTypes); + +// // ActionTypes +// ActionTypes translatedActionTypes; +// foreach (ActionType actionType, deviceClass.actionTypes()) { +// QString translated = translator->translate(plugin->pluginName().toUtf8(), actionType.displayName().toUtf8()); +// if (!translated.isEmpty()) { +// actionType.setDisplayName(translated); +// } +// ParamTypes translatedEventParamTypes; +// foreach (ParamType paramType, actionType.paramTypes()) { +// translated = translator->translate(plugin->pluginName().toUtf8(), paramType.displayName().toUtf8()); +// if (!translated.isEmpty()) { +// paramType.setDisplayName(translated); +// } +// translatedEventParamTypes.append(paramType); +// } +// actionType.setParamTypes(translatedEventParamTypes); +// translatedActionTypes.append(actionType); +// } +// translatedDeviceClass.setActionTypes(translatedActionTypes); +// return translatedDeviceClass; +//} + +QString Translator::translate(const PluginId &pluginId, const QString &string, const QLocale &locale) +{ + DevicePlugin *plugin = m_deviceManager->plugin(pluginId); + + if (!m_translatorContexts.contains(plugin->pluginId()) || !m_translatorContexts.value(plugin->pluginId()).translators.contains(locale)) { + loadTranslator(plugin, locale); + } + + QTranslator* translator = m_translatorContexts.value(plugin->pluginId()).translators.value(locale); + QString translatedString = translator->translate(plugin->pluginName().toUtf8(), string.toUtf8()); + return translatedString.isEmpty() ? string : translatedString; +} + +void Translator::loadTranslator(DevicePlugin *plugin, const QLocale &locale) +{ + if (!m_translatorContexts.contains(plugin->pluginId())) { + // Create default translator for this plugin + TranslatorContext defaultCtx; + defaultCtx.pluginId = plugin->pluginId(); + defaultCtx.translators.insert(QLocale("en_US"), new QTranslator()); + m_translatorContexts.insert(plugin->pluginId(), defaultCtx); + if (locale == QLocale("en_US")) { + return; + } + } + + // check if there are local translations + QTranslator* translator = new QTranslator(); + QString pluginId = plugin->pluginId().toString().remove(QRegExp("[{}]")); + + bool loaded = false; + foreach (const QString &pluginPath, qgetenv("NYMEA_PLUGINS_PATH").split(':')) { + if (translator->load(locale, pluginId, "-", QDir(pluginPath + "/translations/").absolutePath(), ".qm")) { + qCDebug(dcTranslations()) << "* Loaded translation" << locale.name() << "for plugin" << plugin->pluginName() << "from" << QDir(pluginPath + "/translations/").absolutePath(); + loaded = true; + break; + } + foreach (const QString &subdir, QDir(pluginPath).entryList(QDir::Dirs | QDir::NoDotAndDotDot)) { + if (translator->load(locale, pluginId, "-", QDir(pluginPath + "/" + subdir + "/translations/").absolutePath(), ".qm")) { + qCDebug(dcTranslations()) << "* Loaded translation" << locale.name() << "for plugin" << plugin->pluginName() << "from" << QDir(pluginPath + "/" + subdir + "/translations/").absolutePath(); + loaded = true; + break; + } + } + if (loaded) { + break; + } + } + + // otherwise use the system translations + if (!loaded && translator->load(locale, pluginId, "-", NymeaSettings::translationsPath(), ".qm")) { + qCDebug(dcTranslations()) << "* Load translation" << locale.name() << "for" << plugin->pluginName() << "from" << NymeaSettings::translationsPath(); + loaded = true; + } + + if (!loaded && locale.name() != "en_US") + qCWarning(dcTranslations()) << "* Could not load translation" << locale.name() << "for plugin" << plugin->pluginName(); + + if (!loaded) { + translator = m_translatorContexts.value(plugin->pluginId()).translators.value(QLocale("en_US")); + } + + if (!m_translatorContexts.contains(plugin->pluginId())) { + TranslatorContext ctx; + ctx.pluginId = plugin->pluginId(); + m_translatorContexts.insert(plugin->pluginId(), ctx); + } + m_translatorContexts[plugin->pluginId()].translators.insert(locale, translator); + +} diff --git a/libnymea/translator.h b/libnymea/translator.h new file mode 100644 index 00000000..aab18e5b --- /dev/null +++ b/libnymea/translator.h @@ -0,0 +1,33 @@ +#ifndef TRANSLATOR_H +#define TRANSLATOR_H + +#include "typeutils.h" +#include "types/deviceclass.h" + +#include + +class DevicePlugin; +class DeviceManager; + +class Translator +{ +public: + Translator(DeviceManager *deviceManager); + ~Translator(); + + QString translate(const PluginId &pluginId, const QString &string, const QLocale &locale); + +private: + void loadTranslator(DevicePlugin *plugin, const QLocale &locale); + +private: + DeviceManager *m_deviceManager = nullptr; + + struct TranslatorContext { + PluginId pluginId; + QHash translators; + }; + QHash m_translatorContexts; +}; + +#endif // TRANSLATOR_H diff --git a/libnymea/types/deviceclass.cpp b/libnymea/types/deviceclass.cpp index 912381da..80b31f8d 100644 --- a/libnymea/types/deviceclass.cpp +++ b/libnymea/types/deviceclass.cpp @@ -485,3 +485,9 @@ QStringList DeviceClass::mandatoryTypeProperties() { return QStringList() << "id" << "name" << "displayName"; } + +QDebug operator<<(QDebug &dbg, const DeviceClass &deviceClass) +{ + dbg << "DeviceClass ID:" << deviceClass.id() << "Name:" << deviceClass.name(); + return dbg; +} diff --git a/libnymea/types/deviceclass.h b/libnymea/types/deviceclass.h index 1e2171ea..43f1eb97 100644 --- a/libnymea/types/deviceclass.h +++ b/libnymea/types/deviceclass.h @@ -207,4 +207,6 @@ private: Q_DECLARE_OPERATORS_FOR_FLAGS(DeviceClass::CreateMethods) +QDebug operator<<(QDebug &dbg, const DeviceClass &deviceClass); + #endif diff --git a/libnymea/types/vendor.cpp b/libnymea/types/vendor.cpp index f56b5ba0..ada2ecdd 100644 --- a/libnymea/types/vendor.cpp +++ b/libnymea/types/vendor.cpp @@ -74,3 +74,8 @@ void Vendor::setDisplayName(const QString &displayName) { m_displayName = displayName; } + +bool Vendor::operator==(const Vendor &other) const +{ + return m_id == other.id(); +} diff --git a/libnymea/types/vendor.h b/libnymea/types/vendor.h index d1283d90..7521aa69 100644 --- a/libnymea/types/vendor.h +++ b/libnymea/types/vendor.h @@ -43,6 +43,8 @@ public: QString displayName() const; void setDisplayName(const QString &displayName); + bool operator==(const Vendor &other) const; + private: VendorId m_id; QString m_name; diff --git a/nymea.pro b/nymea.pro index ccb07bda..85015b52 100644 --- a/nymea.pro +++ b/nymea.pro @@ -32,6 +32,7 @@ lupdate.commands = lupdate -recursive -no-obsolete $$_FILE_; lrelease.depends = FORCE lrelease.commands = lrelease $$_FILE_; \ rsync -a $$top_srcdir/translations/*.qm $$top_builddir/translations/; +first.depends = $(first) lrelease # Install translation files translations.path = /usr/share/nymea/translations diff --git a/server/main.cpp b/server/main.cpp index 5b26f6bd..b2548f2f 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -131,7 +131,8 @@ int main(int argc, char *argv[]) "AWSTraffic", "BluetoothServer", "BluetoothServerTraffic", - "Mqtt" + "Mqtt", + "Translations" }; QStringList loggingFiltersPlugins;