diff --git a/include/Configuration.h b/include/Configuration.h index 6e98b34..cf1beca 100644 --- a/include/Configuration.h +++ b/include/Configuration.h @@ -109,6 +109,7 @@ struct CONFIG_T { bool Display_ScreenSaver; uint8_t Display_Rotation; uint8_t Display_Contrast; + uint8_t Display_Language; }; class ConfigurationClass { diff --git a/include/Display_Graphic.h b/include/Display_Graphic.h index 18c1c3a..ac0512d 100644 --- a/include/Display_Graphic.h +++ b/include/Display_Graphic.h @@ -20,6 +20,7 @@ public: void loop(); void setContrast(uint8_t contrast); void setOrientation(uint8_t rotation = DISPLAY_ROTATION); + void setLanguage(uint8_t language); void setStartupDisplay(); bool enablePowerSafe = true; @@ -33,6 +34,7 @@ private: U8G2* _display; DisplayType_t _display_type = DisplayType_t::None; + uint8_t _display_language = DISPLAY_LANGUAGE; uint8_t _mExtra; uint16_t _period = 1000; uint16_t _interval = 60000; // interval at which to power save (milliseconds) diff --git a/include/defaults.h b/include/defaults.h index 70ab2a3..9046aaf 100644 --- a/include/defaults.h +++ b/include/defaults.h @@ -91,4 +91,5 @@ #define DISPLAY_POWERSAFE true #define DISPLAY_SCREENSAVER true #define DISPLAY_ROTATION 2 -#define DISPLAY_CONTRAST 60 \ No newline at end of file +#define DISPLAY_CONTRAST 60 +#define DISPLAY_LANGUAGE 0 \ No newline at end of file diff --git a/src/Configuration.cpp b/src/Configuration.cpp index 21df4f5..f64a57c 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -95,6 +95,7 @@ bool ConfigurationClass::write() display["screensaver"] = config.Display_ScreenSaver; display["rotation"] = config.Display_Rotation; display["contrast"] = config.Display_Contrast; + display["language"] = config.Display_Language; JsonArray inverters = doc.createNestedArray("inverters"); for (uint8_t i = 0; i < INV_MAX_COUNT; i++) { @@ -237,6 +238,7 @@ bool ConfigurationClass::read() config.Display_ScreenSaver = display["screensaver"] | DISPLAY_SCREENSAVER; config.Display_Rotation = display["rotation"] | DISPLAY_ROTATION; config.Display_Contrast = display["contrast"] | DISPLAY_CONTRAST; + config.Display_Language = display["language"] | DISPLAY_LANGUAGE; JsonArray inverters = doc["inverters"]; for (uint8_t i = 0; i < INV_MAX_COUNT; i++) { diff --git a/src/Display_Graphic.cpp b/src/Display_Graphic.cpp index 765ee95..d098c50 100644 --- a/src/Display_Graphic.cpp +++ b/src/Display_Graphic.cpp @@ -11,6 +11,25 @@ std::map { DisplayType_t::SH1106, [](uint8_t reset, uint8_t clock, uint8_t data, uint8_t cs) { return new U8G2_SH1106_128X64_NONAME_F_HW_I2C(U8G2_R0, reset, clock, data); } }, }; +// Language defintion, respect order in languages[] and translation lists +#define I18N_LOCALE_EN 0 +#define I18N_LOCALE_DE 1 +#define I18N_LOCALE_FR 2 + +// Languages supported. Note: the order is important and must match locale_translations.h +const uint8_t languages[] = { + I18N_LOCALE_EN, + I18N_LOCALE_DE, + I18N_LOCALE_FR +}; + +static const char* const i18n_offline[] = { "Offline", "Offline", "Offline" }; +static const char* const i18n_current_power_w[] = { "%3.0f W", "%3.0f W", "%3.0f W" }; +static const char* const i18n_current_power_kw[] = { "%2.1f kW", "%2.1f kW", "%2.1f kW" }; +static const char* const i18n_yield_today_wh[] = { "today: %4.0f Wh", "Heute: %4.0f Wh", "auj.: %4.0f Wh" }; +static const char* const i18n_yield_total_kwh[] = { "total: %.1f kWh", "Ges.: %.1f kWh", "total: %.1f kWh" }; +static const char* const i18n_date_format[] = { "%m/%d/%Y %H:%M", "%d.%m.%Y %H:%M", "%d/%m/%Y %H:%M" }; + DisplayGraphicClass::DisplayGraphicClass() { } @@ -95,6 +114,11 @@ void DisplayGraphicClass::setOrientation(uint8_t rotation) calcLineHeights(); } +void DisplayGraphicClass::setLanguage(uint8_t language) +{ + _display_language = language < sizeof(languages) / sizeof(languages[0]) ? language : DISPLAY_LANGUAGE; +} + void DisplayGraphicClass::setStartupDisplay() { if (_display_type == DisplayType_t::None) { @@ -120,9 +144,9 @@ void DisplayGraphicClass::loop() if (Datastore.isAtLeastOneReachable) { _display->setPowerSave(false); if (Datastore.totalAcPowerEnabled > 999) { - snprintf(_fmtText, sizeof(_fmtText), "%2.1f kW", (Datastore.totalAcPowerEnabled / 1000)); + snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_kw[_display_language], (Datastore.totalAcPowerEnabled / 1000)); } else { - snprintf(_fmtText, sizeof(_fmtText), "%3.0f W", Datastore.totalAcPowerEnabled); + snprintf(_fmtText, sizeof(_fmtText), i18n_current_power_w[_display_language], Datastore.totalAcPowerEnabled); } printText(_fmtText, 0); _previousMillis = millis(); @@ -131,7 +155,7 @@ void DisplayGraphicClass::loop() //=====> Offline =========== else { - printText("offline", 0); + printText(i18n_offline[_display_language], 0); // check if it's time to enter power saving mode if (millis() - _previousMillis >= (_interval * 2)) { _display->setPowerSave(enablePowerSafe); @@ -140,10 +164,10 @@ void DisplayGraphicClass::loop() //<======================= //=====> Today & Total Production ======= - snprintf(_fmtText, sizeof(_fmtText), "today: %4.0f Wh", Datastore.totalAcYieldDayEnabled); + snprintf(_fmtText, sizeof(_fmtText), i18n_yield_today_wh[_display_language], Datastore.totalAcYieldDayEnabled); printText(_fmtText, 1); - snprintf(_fmtText, sizeof(_fmtText), "total: %.1f kWh", Datastore.totalAcYieldTotalEnabled); + snprintf(_fmtText, sizeof(_fmtText), i18n_yield_total_kwh[_display_language], Datastore.totalAcYieldTotalEnabled); printText(_fmtText, 2); //<======================= @@ -153,7 +177,7 @@ void DisplayGraphicClass::loop() } else { // Get current time time_t now = time(nullptr); - strftime(_fmtText, sizeof(_fmtText), "%a %d.%m.%Y %H:%M", localtime(&now)); + strftime(_fmtText, sizeof(_fmtText), i18n_date_format[_display_language], localtime(&now)); printText(_fmtText, 3); } _display->sendBuffer(); diff --git a/src/WebApi_device.cpp b/src/WebApi_device.cpp index 0903bee..e64f80a 100644 --- a/src/WebApi_device.cpp +++ b/src/WebApi_device.cpp @@ -80,6 +80,7 @@ void WebApiDeviceClass::onDeviceAdminGet(AsyncWebServerRequest* request) display["power_safe"] = config.Display_PowerSafe; display["screensaver"] = config.Display_ScreenSaver; display["contrast"] = config.Display_Contrast; + display["language"] = config.Display_Language; response->setLength(); request->send(response); @@ -149,11 +150,13 @@ void WebApiDeviceClass::onDeviceAdminPost(AsyncWebServerRequest* request) config.Display_PowerSafe = root["display"]["power_safe"].as(); config.Display_ScreenSaver = root["display"]["screensaver"].as(); config.Display_Contrast = root["display"]["contrast"].as(); + config.Display_Language = root["display"]["language"].as(); Display.setOrientation(config.Display_Rotation); Display.enablePowerSafe = config.Display_PowerSafe; Display.enableScreensaver = config.Display_ScreenSaver; Display.setContrast(config.Display_Contrast); + Display.setLanguage(config.Display_Language); Configuration.write(); diff --git a/src/main.cpp b/src/main.cpp index f58efb5..d12a357 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -120,6 +120,7 @@ void setup() Display.enablePowerSafe = config.Display_PowerSafe; Display.enableScreensaver = config.Display_ScreenSaver; Display.setContrast(config.Display_Contrast); + Display.setLanguage(config.Display_Language); Display.setStartupDisplay(); MessageOutput.println("done"); diff --git a/webapp/src/locales/de.json b/webapp/src/locales/de.json index 871b324..74c01e6 100644 --- a/webapp/src/locales/de.json +++ b/webapp/src/locales/de.json @@ -530,6 +530,10 @@ "Rotation": "Rotation:", "rot0": "Keine Rotation", "rot90": "90 Grad Drehung", + "DisplayLanguage": "Displaysprache:", + "en": "Englisch", + "de": "Deutsch", + "fr": "Französisch", "rot180": "180 Grad Drehung", "rot270": "270 Grad Drehung", "Save": "@:dtuadmin.Save" diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index 130d948..bbbbf91 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -532,6 +532,10 @@ "rot90": "90 degree rotation", "rot180": "180 degree rotation", "rot270": "270 degree rotation", + "DisplayLanguage": "Display language:", + "en": "English", + "de": "German", + "fr": "French", "Save": "@:dtuadmin.Save" }, "pininfo": { diff --git a/webapp/src/locales/fr.json b/webapp/src/locales/fr.json index eca44fa..e6bcd5e 100644 --- a/webapp/src/locales/fr.json +++ b/webapp/src/locales/fr.json @@ -532,6 +532,10 @@ "rot90": "90 degree rotation", "rot180": "180 degree rotation", "rot270": "270 degree rotation", + "DisplayLanguage": "Langue d'affichage", + "en": "Anglais", + "de": "Allemand", + "fr": "Français", "Save": "@:dtuadmin.Save" }, "pininfo": { diff --git a/webapp/src/types/DeviceConfig.ts b/webapp/src/types/DeviceConfig.ts index 8bd87f7..8b77c5b 100644 --- a/webapp/src/types/DeviceConfig.ts +++ b/webapp/src/types/DeviceConfig.ts @@ -5,6 +5,7 @@ export interface Display { power_safe: boolean; screensaver: boolean; contrast: number; + language: number; } export interface DeviceConfig { diff --git a/webapp/src/views/DeviceAdminView.vue b/webapp/src/views/DeviceAdminView.vue index 574105e..090bcfb 100644 --- a/webapp/src/views/DeviceAdminView.vue +++ b/webapp/src/views/DeviceAdminView.vue @@ -56,6 +56,19 @@ v-model="deviceConfigList.display.screensaver" type="checkbox" :tooltip="$t('deviceadmin.ScreensaverHint')" /> +
+ +
+ +
+
+