diff --git a/.gitignore b/.gitignore index d6f917cfc4..308181d328 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,9 @@ src/arm-none-eabi node_modules package.json package-lock.json + +# Local simulation and container virtualization artifacts +build_lv_sim/ +InfiniSim/ +.podman/ +.containers/ diff --git a/doc/localization.md b/doc/localization.md new file mode 100644 index 0000000000..252e8e1055 --- /dev/null +++ b/doc/localization.md @@ -0,0 +1,60 @@ +# Localization + +InfiniTime has a small static localization layer for user interface strings. It is designed for firmware use: + +- strings are addressed by `Pinetime::Applications::Localization::StringId` +- the selected language is `Pinetime::Applications::Localization::Language` +- translations are stored in fixed `constexpr` tables in `src/displayapp/localization/Localization.cpp` +- callers use `Translate(language, stringId)` to get a `const char*` +- no dynamic allocation is used by the localization layer + +The currently supported languages are English and Spanish. + +Spanish uses accented characters such as `á`, `é`, `í`, `ó`, `ú`, `ñ`, `¿` and `¡`. The built-in UI font range is configured in: + +```text +src/displayapp/fonts/fonts.json +``` + +`jetbrains_mono_bold_20` includes the Latin-1 range so normal UI labels can render Spanish text correctly on device. + +## Where Strings Are Defined + +String IDs and supported languages are declared in: + +```text +src/displayapp/localization/Localization.h +``` + +English and Spanish string tables are defined in: + +```text +src/displayapp/localization/Localization.cpp +``` + +The settings controller stores the selected language in the persisted settings data. English is the default. + +## Adding a New Language + +To add a language: + +1. Add a value to the `Language` enum before `Count`. +2. Add a translation table in `Localization.cpp`. +3. Add that table to the `translations` array in the same enum order. +4. Add the language to the selector options in `src/displayapp/screens/settings/SettingLanguage.cpp`. +5. Add translations for every existing `StringId`. +6. Make sure any new characters required by the language are present in the active UI font ranges. + +The translation tables use `std::array`. This makes missing entries fail at compile time when the table size no longer matches the number of string IDs. + +## Settings Menu Usage + +The Settings app stores menu entries as string IDs instead of hardcoded labels. `src/displayapp/screens/List.cpp` translates those IDs with the current language when the list screen is created. + +The language selector is implemented in: + +```text +src/displayapp/screens/settings/SettingLanguage.cpp +``` + +It uses the existing `CheckboxList` pattern and saves the selected language through the settings controller. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e4a354df64..7a0a77a641 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -398,6 +398,7 @@ list(APPEND SOURCE_FILES displayapp/screens/Alarm.cpp displayapp/screens/Styles.cpp displayapp/screens/WeatherSymbols.cpp + displayapp/localization/Localization.cpp displayapp/Colors.cpp displayapp/widgets/Counter.cpp displayapp/widgets/PageIndicator.cpp @@ -421,6 +422,7 @@ list(APPEND SOURCE_FILES displayapp/screens/settings/SettingShakeThreshold.cpp displayapp/screens/settings/SettingBluetooth.cpp displayapp/screens/settings/SettingOTA.cpp + displayapp/screens/settings/SettingLanguage.cpp ## Watch faces displayapp/screens/WatchFaceAnalog.cpp @@ -611,6 +613,7 @@ set(INCLUDE_FILES displayapp/screens/FirmwareValidation.h displayapp/screens/ApplicationList.h displayapp/screens/CheckboxList.h + displayapp/localization/Localization.h displayapp/Apps.h displayapp/screens/Notifications.h displayapp/screens/HeartRate.h @@ -671,6 +674,7 @@ set(INCLUDE_FILES systemtask/SystemMonitor.h systemtask/WakeLock.h displayapp/screens/Symbols.h + displayapp/screens/settings/SettingLanguage.h drivers/TwiMaster.h heartratetask/HeartRateTask.h components/heartrate/Ppg.h @@ -1143,4 +1147,3 @@ endif() if(BUILD_RESOURCES) add_subdirectory(resources) endif() - diff --git a/src/components/datetime/DateTimeController.cpp b/src/components/datetime/DateTimeController.cpp index 2ef0ef22f1..7f20b92e5c 100644 --- a/src/components/datetime/DateTimeController.cpp +++ b/src/components/datetime/DateTimeController.cpp @@ -11,9 +11,79 @@ namespace { constexpr const char* const DaysStringShortLow[] = {"--", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; constexpr const char* const DaysString[] = {"--", "MONDAY", "TUESDAY", "WEDNESDAY", "THURSDAY", "FRIDAY", "SATURDAY", "SUNDAY"}; constexpr const char* const DaysStringLow[] = {"--", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"}; + constexpr const char* const DaysStringShortEs[] = {"--", "LUN", "MAR", "MIE", "JUE", "VIE", "SAB", "DOM"}; + constexpr const char* const DaysStringShortLowEs[] = {"--", "Lun", "Mar", "Mie", "Jue", "Vie", "Sab", "Dom"}; + constexpr const char* const DaysStringEs[] = {"--", "LUNES", "MARTES", "MIERCOLES", "JUEVES", "VIERNES", "SABADO", "DOMINGO"}; + constexpr const char* const DaysStringLowEs[] = {"--", "Lunes", "Martes", "Miercoles", "Jueves", "Viernes", "Sabado", "Domingo"}; + constexpr const char* const DaysStringShortPt[] = {"--", "SEG", "TER", "QUA", "QUI", "SEX", "SAB", "DOM"}; + constexpr const char* const DaysStringShortLowPt[] = {"--", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab", "Dom"}; + constexpr const char* const DaysStringPt[] = {"--", "SEGUNDA", "TERCA", "QUARTA", "QUINTA", "SEXTA", "SABADO", "DOMINGO"}; + constexpr const char* const DaysStringLowPt[] = {"--", "Segunda", "Terca", "Quarta", "Quinta", "Sexta", "Sabado", "Domingo"}; + constexpr const char* const DaysStringShortRu[] = {"--", "ПН", "ВТ", "СР", "ЧТ", "ПТ", "СБ", "ВС"}; + constexpr const char* const DaysStringShortLowRu[] = {"--", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"}; + constexpr const char* const DaysStringRu[] = {"--", "ПОНЕДЕЛЬНИК", "ВТОРНИК", "СРЕДА", "ЧЕТВЕРГ", "ПЯТНИЦА", "СУББОТА", "ВОСКРЕСЕНЬЕ"}; + constexpr const char* const DaysStringLowRu[] = {"--", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота", "Воскресенье"}; + constexpr const char* const DaysStringShortFr[] = {"--", "LUN", "MAR", "MER", "JEU", "VEN", "SAM", "DIM"}; + constexpr const char* const DaysStringShortLowFr[] = {"--", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam", "Dim"}; + constexpr const char* const DaysStringFr[] = {"--", "LUNDI", "MARDI", "MERCREDI", "JEUDI", "VENDREDI", "SAMEDI", "DIMANCHE"}; + constexpr const char* const DaysStringLowFr[] = {"--", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche"}; + constexpr const char* const DaysStringShortDe[] = {"--", "MO", "DI", "MI", "DO", "FR", "SA", "SO"}; + constexpr const char* const DaysStringShortLowDe[] = {"--", "Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"}; + constexpr const char* const DaysStringDe[] = {"--", "MONTAG", "DIENSTAG", "MITTWOCH", "DONNERSTAG", "FREITAG", "SAMSTAG", "SONNTAG"}; + constexpr const char* const DaysStringLowDe[] = {"--", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"}; + constexpr const char* const DaysStringShortIt[] = {"--", "LUN", "MAR", "MER", "GIO", "VEN", "SAB", "DOM"}; + constexpr const char* const DaysStringShortLowIt[] = {"--", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab", "Dom"}; + constexpr const char* const DaysStringIt[] = {"--", "LUNEDI", "MARTEDI", "MERCOLEDI", "GIOVEDI", "VENERDI", "SABATO", "DOMENICA"}; + constexpr const char* const DaysStringLowIt[] = {"--", "Lunedi", "Martedi", "Mercoledi", "Giovedi", "Venerdi", "Sabato", "Domenica"}; + constexpr const char* const DaysStringShortTr[] = {"--", "PZT", "SAL", "CAR", "PER", "CUM", "CMT", "PAZ"}; + constexpr const char* const DaysStringShortLowTr[] = {"--", "Pzt", "Sal", "Car", "Per", "Cum", "Cmt", "Paz"}; + constexpr const char* const DaysStringTr[] = {"--", "PAZARTESI", "SALI", "CARSAMBA", "PERSEMBE", "CUMA", "CUMARTESI", "PAZAR"}; + constexpr const char* const DaysStringLowTr[] = {"--", "Pazartesi", "Sali", "Carsamba", "Persembe", "Cuma", "Cumartesi", "Pazar"}; + constexpr const char* const DaysStringShortPl[] = {"--", "PON", "WTO", "SRO", "CZW", "PIA", "SOB", "NIE"}; + constexpr const char* const DaysStringShortLowPl[] = {"--", "Pon", "Wto", "Sro", "Czw", "Pia", "Sob", "Nie"}; + constexpr const char* const DaysStringPl[] = {"--", "PONIEDZIALEK", "WTOREK", "SRODA", "CZWARTEK", "PIATEK", "SOBOTA", "NIEDZIELA"}; + constexpr const char* const DaysStringLowPl[] = {"--", "Poniedzialek", "Wtorek", "Sroda", "Czwartek", "Piatek", "Sobota", "Niedziela"}; + constexpr const char* const DaysStringShortCa[] = {"--", "DLL", "DMT", "DMC", "DJS", "DVN", "DSB", "DGN"}; + constexpr const char* const DaysStringShortLowCa[] = {"--", "Dll", "Dmt", "Dmc", "Djs", "Dvn", "Dsb", "Dgn"}; + constexpr const char* const DaysStringCa[] = {"--", "DILLUNS", "DIMARTS", "DIMECRES", "DIJOUS", "DIVENDRES", "DISSABTE", "DIUMENGE"}; + constexpr const char* const DaysStringLowCa[] = {"--", "Dilluns", "Dimarts", "Dimecres", "Dijous", "Divendres", "Dissabte", "Diumenge"}; + constexpr const char* const DaysStringShortEu[] = {"--", "AL", "AR", "AZ", "OG", "OR", "LR", "IG"}; + constexpr const char* const DaysStringShortLowEu[] = {"--", "Al", "Ar", "Az", "Og", "Or", "Lr", "Ig"}; + constexpr const char* const DaysStringEu[] = {"--", "ASTELEHENA", "ASTEARTEA", "ASTEAZKENA", "OSTEGUNA", "OSTIRALA", "LARUNBATA", "IGANDEA"}; + constexpr const char* const DaysStringLowEu[] = {"--", "Astelehena", "Asteartea", "Asteazkena", "Osteguna", "Ostirala", "Larunbata", "Igandea"}; constexpr const char* const MonthsString[] = {"--", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"}; constexpr const char* const MonthsStringLow[] = {"--", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + constexpr const char* const MonthsStringEs[] = {"--", "ENE", "FEB", "MAR", "ABR", "MAY", "JUN", "JUL", "AGO", "SEP", "OCT", "NOV", "DIC"}; + constexpr const char* const MonthsStringLowEs[] = + {"--", "Ene", "Feb", "Mar", "Abr", "May", "Jun", "Jul", "Ago", "Sep", "Oct", "Nov", "Dic"}; + constexpr const char* const MonthsStringPt[] = {"--", "JAN", "FEV", "MAR", "ABR", "MAI", "JUN", "JUL", "AGO", "SET", "OUT", "NOV", "DEZ"}; + constexpr const char* const MonthsStringLowPt[] = + {"--", "Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"}; + constexpr const char* const MonthsStringRu[] = {"--", "ЯНВ", "ФЕВ", "МАР", "АПР", "МАЙ", "ИЮН", "ИЮЛ", "АВГ", "СЕН", "ОКТ", "НОЯ", "ДЕК"}; + constexpr const char* const MonthsStringLowRu[] = + {"--", "Янв", "Фев", "Мар", "Апр", "Май", "Июн", "Июл", "Авг", "Сен", "Окт", "Ноя", "Дек"}; + constexpr const char* const MonthsStringFr[] = {"--", "JAN", "FEV", "MAR", "AVR", "MAI", "JUN", "JUL", "AOU", "SEP", "OCT", "NOV", "DEC"}; + constexpr const char* const MonthsStringLowFr[] = + {"--", "Jan", "Fev", "Mar", "Avr", "Mai", "Jun", "Jul", "Aou", "Sep", "Oct", "Nov", "Dec"}; + constexpr const char* const MonthsStringDe[] = {"--", "JAN", "FEB", "MAR", "APR", "MAI", "JUN", "JUL", "AUG", "SEP", "OKT", "NOV", "DEZ"}; + constexpr const char* const MonthsStringLowDe[] = + {"--", "Jan", "Feb", "Mar", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"}; + constexpr const char* const MonthsStringIt[] = {"--", "GEN", "FEB", "MAR", "APR", "MAG", "GIU", "LUG", "AGO", "SET", "OTT", "NOV", "DIC"}; + constexpr const char* const MonthsStringLowIt[] = + {"--", "Gen", "Feb", "Mar", "Apr", "Mag", "Giu", "Lug", "Ago", "Set", "Ott", "Nov", "Dic"}; + constexpr const char* const MonthsStringTr[] = {"--", "OCA", "SUB", "MAR", "NIS", "MAY", "HAZ", "TEM", "AGU", "EYL", "EKI", "KAS", "ARA"}; + constexpr const char* const MonthsStringLowTr[] = + {"--", "Oca", "Sub", "Mar", "Nis", "May", "Haz", "Tem", "Agu", "Eyl", "Eki", "Kas", "Ara"}; + constexpr const char* const MonthsStringPl[] = {"--", "STY", "LUT", "MAR", "KWI", "MAJ", "CZE", "LIP", "SIE", "WRZ", "PAZ", "LIS", "GRU"}; + constexpr const char* const MonthsStringLowPl[] = + {"--", "Sty", "Lut", "Mar", "Kwi", "Maj", "Cze", "Lip", "Sie", "Wrz", "Paz", "Lis", "Gru"}; + constexpr const char* const MonthsStringCa[] = {"--", "GEN", "FEB", "MAR", "ABR", "MAI", "JUN", "JUL", "AGO", "SET", "OCT", "NOV", "DES"}; + constexpr const char* const MonthsStringLowCa[] = + {"--", "Gen", "Feb", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Oct", "Nov", "Des"}; + constexpr const char* const MonthsStringEu[] = {"--", "URT", "OTS", "MAR", "API", "MAI", "EKA", "UZT", "ABU", "IRA", "URR", "AZA", "ABE"}; + constexpr const char* const MonthsStringLowEu[] = + {"--", "Urt", "Ots", "Mar", "Api", "Mai", "Eka", "Uzt", "Abu", "Ira", "Urr", "Aza", "Abe"}; constexpr int compileTimeAtoi(const char* str) { int result = 0; @@ -139,27 +209,183 @@ void DateTime::UpdateTime(uint32_t systickCounter, bool forceUpdate) { } const char* DateTime::MonthShortToString() const { - return MonthsString[static_cast(Month())]; + const auto month = static_cast(Month()); + switch (settingsController.GetLanguage()) { + case Controllers::Settings::Language::Spanish: + return MonthsStringEs[month]; + case Controllers::Settings::Language::Portuguese: + return MonthsStringPt[month]; + case Controllers::Settings::Language::Russian: + return MonthsStringRu[month]; + case Controllers::Settings::Language::French: + return MonthsStringFr[month]; + case Controllers::Settings::Language::German: + return MonthsStringDe[month]; + case Controllers::Settings::Language::Italian: + return MonthsStringIt[month]; + case Controllers::Settings::Language::Turkish: + return MonthsStringTr[month]; + case Controllers::Settings::Language::Polish: + return MonthsStringPl[month]; + case Controllers::Settings::Language::Catalan: + return MonthsStringCa[month]; + case Controllers::Settings::Language::Basque: + return MonthsStringEu[month]; + default: + return MonthsString[month]; + } } const char* DateTime::DayOfWeekShortToString() const { - return DaysStringShort[static_cast(DayOfWeek())]; + const auto day = static_cast(DayOfWeek()); + switch (settingsController.GetLanguage()) { + case Controllers::Settings::Language::Spanish: + return DaysStringShortEs[day]; + case Controllers::Settings::Language::Portuguese: + return DaysStringShortPt[day]; + case Controllers::Settings::Language::Russian: + return DaysStringShortRu[day]; + case Controllers::Settings::Language::French: + return DaysStringShortFr[day]; + case Controllers::Settings::Language::German: + return DaysStringShortDe[day]; + case Controllers::Settings::Language::Italian: + return DaysStringShortIt[day]; + case Controllers::Settings::Language::Turkish: + return DaysStringShortTr[day]; + case Controllers::Settings::Language::Polish: + return DaysStringShortPl[day]; + case Controllers::Settings::Language::Catalan: + return DaysStringShortCa[day]; + case Controllers::Settings::Language::Basque: + return DaysStringShortEu[day]; + default: + return DaysStringShort[day]; + } } const char* DateTime::DayOfWeekToString() const { - return DaysString[static_cast(DayOfWeek())]; + const auto day = static_cast(DayOfWeek()); + switch (settingsController.GetLanguage()) { + case Controllers::Settings::Language::Spanish: + return DaysStringEs[day]; + case Controllers::Settings::Language::Portuguese: + return DaysStringPt[day]; + case Controllers::Settings::Language::Russian: + return DaysStringRu[day]; + case Controllers::Settings::Language::French: + return DaysStringFr[day]; + case Controllers::Settings::Language::German: + return DaysStringDe[day]; + case Controllers::Settings::Language::Italian: + return DaysStringIt[day]; + case Controllers::Settings::Language::Turkish: + return DaysStringTr[day]; + case Controllers::Settings::Language::Polish: + return DaysStringPl[day]; + case Controllers::Settings::Language::Catalan: + return DaysStringCa[day]; + case Controllers::Settings::Language::Basque: + return DaysStringEu[day]; + default: + return DaysString[day]; + } } const char* DateTime::MonthShortToStringLow(Months month) { - return MonthsStringLow[static_cast(month)]; + return MonthShortToStringLow(month, Controllers::Settings::Language::English); +} + +const char* DateTime::MonthShortToStringLow(Months month, Controllers::Settings::Language language) { + const auto monthIndex = static_cast(month); + switch (language) { + case Controllers::Settings::Language::Spanish: + return MonthsStringLowEs[monthIndex]; + case Controllers::Settings::Language::Portuguese: + return MonthsStringLowPt[monthIndex]; + case Controllers::Settings::Language::Russian: + return MonthsStringLowRu[monthIndex]; + case Controllers::Settings::Language::French: + return MonthsStringLowFr[monthIndex]; + case Controllers::Settings::Language::German: + return MonthsStringLowDe[monthIndex]; + case Controllers::Settings::Language::Italian: + return MonthsStringLowIt[monthIndex]; + case Controllers::Settings::Language::Turkish: + return MonthsStringLowTr[monthIndex]; + case Controllers::Settings::Language::Polish: + return MonthsStringLowPl[monthIndex]; + case Controllers::Settings::Language::Catalan: + return MonthsStringLowCa[monthIndex]; + case Controllers::Settings::Language::Basque: + return MonthsStringLowEu[monthIndex]; + default: + return MonthsStringLow[monthIndex]; + } } const char* DateTime::DayOfWeekShortToStringLow(Days day) { - return DaysStringShortLow[static_cast(day)]; + return DayOfWeekShortToStringLow(day, Controllers::Settings::Language::English); +} + +const char* DateTime::DayOfWeekShortToStringLow(Days day, Controllers::Settings::Language language) { + const auto dayIndex = static_cast(day); + switch (language) { + case Controllers::Settings::Language::Spanish: + return DaysStringShortLowEs[dayIndex]; + case Controllers::Settings::Language::Portuguese: + return DaysStringShortLowPt[dayIndex]; + case Controllers::Settings::Language::Russian: + return DaysStringShortLowRu[dayIndex]; + case Controllers::Settings::Language::French: + return DaysStringShortLowFr[dayIndex]; + case Controllers::Settings::Language::German: + return DaysStringShortLowDe[dayIndex]; + case Controllers::Settings::Language::Italian: + return DaysStringShortLowIt[dayIndex]; + case Controllers::Settings::Language::Turkish: + return DaysStringShortLowTr[dayIndex]; + case Controllers::Settings::Language::Polish: + return DaysStringShortLowPl[dayIndex]; + case Controllers::Settings::Language::Catalan: + return DaysStringShortLowCa[dayIndex]; + case Controllers::Settings::Language::Basque: + return DaysStringShortLowEu[dayIndex]; + default: + return DaysStringShortLow[dayIndex]; + } } const char* DateTime::DayOfWeekToStringLow(Days day) { - return DaysStringLow[static_cast(day)]; + return DayOfWeekToStringLow(day, Controllers::Settings::Language::English); +} + +const char* DateTime::DayOfWeekToStringLow(Days day, Controllers::Settings::Language language) { + const auto dayIndex = static_cast(day); + switch (language) { + case Controllers::Settings::Language::Spanish: + return DaysStringLowEs[dayIndex]; + case Controllers::Settings::Language::Portuguese: + return DaysStringLowPt[dayIndex]; + case Controllers::Settings::Language::Russian: + return DaysStringLowRu[dayIndex]; + case Controllers::Settings::Language::French: + return DaysStringLowFr[dayIndex]; + case Controllers::Settings::Language::German: + return DaysStringLowDe[dayIndex]; + case Controllers::Settings::Language::Italian: + return DaysStringLowIt[dayIndex]; + case Controllers::Settings::Language::Turkish: + return DaysStringLowTr[dayIndex]; + case Controllers::Settings::Language::Polish: + return DaysStringLowPl[dayIndex]; + case Controllers::Settings::Language::Catalan: + return DaysStringLowCa[dayIndex]; + case Controllers::Settings::Language::Basque: + return DaysStringLowEu[dayIndex]; + default: + return DaysStringLow[dayIndex]; + } } void DateTime::Register(Pinetime::System::SystemTask* systemTask) { diff --git a/src/components/datetime/DateTimeController.h b/src/components/datetime/DateTimeController.h index 33fef6be09..dfbb646509 100644 --- a/src/components/datetime/DateTimeController.h +++ b/src/components/datetime/DateTimeController.h @@ -123,8 +123,11 @@ namespace Pinetime { const char* DayOfWeekShortToString() const; const char* DayOfWeekToString() const; static const char* MonthShortToStringLow(Months month); + static const char* MonthShortToStringLow(Months month, Controllers::Settings::Language language); static const char* DayOfWeekShortToStringLow(Days day); + static const char* DayOfWeekShortToStringLow(Days day, Controllers::Settings::Language language); static const char* DayOfWeekToStringLow(Days day); + static const char* DayOfWeekToStringLow(Days day, Controllers::Settings::Language language); std::chrono::time_point CurrentDateTime(); diff --git a/src/components/settings/Settings.cpp b/src/components/settings/Settings.cpp index 1ae00a2dbc..e7b45d03f3 100644 --- a/src/components/settings/Settings.cpp +++ b/src/components/settings/Settings.cpp @@ -33,6 +33,10 @@ void Settings::LoadSettingsFromFile() { fs.FileClose(&settingsFile); if (bufferSettings.version == settingsVersion) { settings = bufferSettings; + if (static_cast(settings.language) >= static_cast(Language::Count)) { + settings.language = Language::English; + settingsChanged = true; + } } } diff --git a/src/components/settings/Settings.h b/src/components/settings/Settings.h index 9133d3fea1..f19e7ff3dd 100644 --- a/src/components/settings/Settings.h +++ b/src/components/settings/Settings.h @@ -6,6 +6,7 @@ #include "components/brightness/BrightnessController.h" #include "components/fs/FS.h" #include "displayapp/apps/Apps.h" +#include "displayapp/localization/Localization.h" #include namespace Pinetime { @@ -41,6 +42,7 @@ namespace Pinetime { enum class PTSWeather : uint8_t { On, Off }; enum class PrideFlag : uint8_t { Gay, Trans, Bi, Lesbian }; enum class DfuAndFsMode : uint8_t { Disabled, Enabled, EnabledTillReboot }; + using Language = Pinetime::Applications::Localization::Language; struct PineTimeStyle { Colors ColorTime = Colors::Teal; @@ -351,10 +353,21 @@ namespace Pinetime { settings.heartRateBackgroundPeriod = newIntervalInSeconds.value(); } + void SetLanguage(Language language) { + if (language != settings.language) { + settingsChanged = true; + } + settings.language = language; + } + + Language GetLanguage() const { + return settings.language; + } + private: Pinetime::Controllers::FS& fs; - static constexpr uint32_t settingsVersion = 0x000a; + static constexpr uint32_t settingsVersion = 0x000b; struct SettingsData { uint32_t version = settingsVersion; @@ -383,6 +396,7 @@ namespace Pinetime { bool dfuAndFsEnabledOnBoot = false; uint16_t heartRateBackgroundPeriod = std::numeric_limits::max(); // Disabled by default + Language language = Language::English; }; SettingsData settings; diff --git a/src/displayapp/DisplayApp.cpp b/src/displayapp/DisplayApp.cpp index 84fa603622..2e9c911751 100644 --- a/src/displayapp/DisplayApp.cpp +++ b/src/displayapp/DisplayApp.cpp @@ -52,6 +52,7 @@ #include "displayapp/screens/settings/SettingShakeThreshold.h" #include "displayapp/screens/settings/SettingBluetooth.h" #include "displayapp/screens/settings/SettingOTA.h" +#include "displayapp/screens/settings/SettingLanguage.h" #include "utility/Math.h" @@ -549,14 +550,14 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio settingsController.SetAppMenu(0); } break; case Apps::Error: - currentScreen = std::make_unique(bootError); + currentScreen = std::make_unique(bootError, settingsController); break; case Apps::FirmwareValidation: - currentScreen = std::make_unique(validator); + currentScreen = std::make_unique(validator, settingsController); break; case Apps::FirmwareUpdate: - currentScreen = std::make_unique(bleController); + currentScreen = std::make_unique(bleController, settingsController); break; case Apps::PassKey: @@ -568,6 +569,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio notificationManager, systemTask->nimble().alertService(), motorController, + settingsController, *systemTask, Screens::Notifications::Modes::Normal); break; @@ -576,6 +578,7 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio notificationManager, systemTask->nimble().alertService(), motorController, + settingsController, *systemTask, Screens::Notifications::Modes::Preview); break; @@ -634,8 +637,11 @@ void DisplayApp::LoadScreen(Apps app, DisplayApp::FullRefreshDirections directio case Apps::SettingOTA: currentScreen = std::make_unique(this, settingsController); break; + case Apps::SettingLanguage: + currentScreen = std::make_unique(this, settingsController); + break; case Apps::BatteryInfo: - currentScreen = std::make_unique(batteryController); + currentScreen = std::make_unique(batteryController, settingsController); break; case Apps::SysInfo: currentScreen = std::make_unique(this, diff --git a/src/displayapp/apps/Apps.h.in b/src/displayapp/apps/Apps.h.in index d440b598d1..a6cbb8a073 100644 --- a/src/displayapp/apps/Apps.h.in +++ b/src/displayapp/apps/Apps.h.in @@ -45,6 +45,7 @@ namespace Pinetime { SettingShakeThreshold, SettingBluetooth, SettingOTA, + SettingLanguage, Error }; diff --git a/src/displayapp/fonts/fonts.json b/src/displayapp/fonts/fonts.json index 3221c2f171..6cef6981bb 100644 --- a/src/displayapp/fonts/fonts.json +++ b/src/displayapp/fonts/fonts.json @@ -3,7 +3,7 @@ "sources": [ { "file": "JetBrainsMono-Bold.ttf", - "range": "0x20-0x7e, 0x410-0x44f, 0xB0" + "range": "0x20-0x7e, 0xA1, 0xBF, 0xC0-0xFF, 0x410-0x44f, 0xB0" }, { "file": "FontAwesome5-Solid+Brands+Regular.woff", diff --git a/src/displayapp/localization/Localization.cpp b/src/displayapp/localization/Localization.cpp new file mode 100644 index 0000000000..0fc738c1aa --- /dev/null +++ b/src/displayapp/localization/Localization.cpp @@ -0,0 +1,2478 @@ +#include "displayapp/localization/Localization.h" + +using namespace Pinetime::Applications::Localization; + +namespace { + using TranslationTable = std::array; + + constexpr TranslationTable english {{ + "", + "Settings", + "Display", + "Wake Up", + "Time format", + "Watch face", + "Steps", + "Heartrate", + "Date & Time", + "Weather", + "Battery", + "Chimes", + "Shake Calib.", + "Firmware", + "Over-the-air", + "Bluetooth", + "About", + "Language", + "English", + "Spanish", + "Portuguese", + "Russian", + "French", + "German", + "Italian", + "Turkish", + "Polish", + "Catalan", + "Basque", + "Back", + "On", + "Off", + "Enabled", + "Disabled", + "Start", + "Stop", + "Pause", + "Reset", + "Set", + "Charging", + "Fully charged", + "Discharging", + "Battery low", + "Battery critical", + "Reading Battery", + "volts", + "Daily steps goal", + "Goal", + "Trip", + "Yest", + "Heart rate BPM", + "Not enough data,\nplease wait...", + "No touch detected", + "Measuring...", + "Stopped", + "Backg. Interval", + "Cont", + "Display timeout", + "Always On", + "Single Tap", + "Double Tap", + "Raise Wrist", + "Shake Wake", + "Lower Wrist", + "Set current time", + "Set current date", + "Metric", + "Imperial", + "Every hour", + "Every 30 mins", + "Till reboot", + "Firmware & files", + "Clear sky", + "Few clouds", + "Scattered clouds", + "Broken clouds", + "Shower rain", + "Rain", + "Thunderstorm", + "Snow", + "Mist", + "Clear", + "Cloudy", + "Showers", + "Thunder", + "Notification", + "Incoming call from", + "Notifications", + "No notifications", + "too large", + "zero division", + "Warning", + "Touch controller error detected.", + "If you encounter problems and your device is under warranty, contact the devices seller.", + "Proceed", + "Version", + "Short Ref", + "This firmware has\nbeen #00ff00 validated#", + "Any reboot will\nrollback to last\nvalidated firmware", + "Validate", + "Rollback", + "Firmware update", + "Waiting...", + "Wake Sensitivity", + "Calibrate", + "Shake!", + "Ready!", + "Connected", + "Disconnected", + "Image OK!", + "Error!", + "You have\nmail!", + "Navigation", + "Score", + "Alarm\nis not\nset.", + "Time to\nalarm:", + "Days", + "Hours", + "Minutes", + "Seconds", + "Once", + "Daily", + "Mon-Fri", + "Heads", + "Tails", + }}; + + constexpr TranslationTable spanish {{ + "", + "Ajustes", + "Pantalla", + "Activar", + "Formato hora", + "Esfera", + "Pasos", + "Pulso", + "Fecha y hora", + "Clima", + "Batería", + "Avisos", + "Calibrar mov.", + "Firmware", + "OTA", + "Bluetooth", + "Acerca de", + "Idioma", + "Inglés", + "Español", + "Portugués", + "Ruso", + "Francés", + "Alemán", + "Italiano", + "Turco", + "Polaco", + "Catalán", + "Euskera", + "Atrás", + "Act.", + "Des.", + "Activo", + "Inactivo", + "Iniciar", + "Parar", + "Pausa", + "Reiniciar", + "Fijar", + "Cargando", + "Carga compl.", + "Descargando", + "Batería baja", + "Bat. crítica", + "Leyendo batería", + "voltios", + "Meta diaria", + "Meta", + "Viaje", + "Ayer", + "Pulso LPM", + "Faltan datos,\nespera...", + "Sin contacto", + "Midiendo...", + "Parado", + "Interv. fondo", + "Cont", + "Espera pantalla", + "Siempre act.", + "Toque simple", + "Doble toque", + "Alzar muñeca", + "Agitar", + "Bajar muñeca", + "Ajustar hora", + "Ajustar fecha", + "Métrico", + "Imperial", + "Cada hora", + "Cada 30 min", + "Hasta reinicio", + "Firmware y arch.", + "Despejado", + "Pocas nubes", + "Nubes dispersas", + "Muy nuboso", + "Chubascos", + "Lluvia", + "Tormenta", + "Nieve", + "Niebla", + "Despej.", + "Nublado", + "Chubasc.", + "Trueno", + "Notificación", + "Llamada de", + "Notificaciones", + "Sin notificaciones", + "muy grande", + "división cero", + "Aviso", + "Error táctil detectado.", + "Si hay problemas y está en garantía, contacta con el vendedor.", + "Continuar", + "Versión", + "Ref corta", + "Firmware\n#00ff00 validado#", + "Al reiniciar\nvolverá al firmware\nvalidado anterior", + "Validar", + "Revertir", + "Act. firmware", + "Esperando...", + "Sensibilidad", + "Calibrar", + "¡Agita!", + "¡Listo!", + "Conectado", + "Desconectado", + "¡Imagen OK!", + "¡Error!", + "¡Tienes\ncorreo!", + "Navegación", + "Puntuación", + "Alarma\nno\nfijada.", + "Tiempo hasta\nalarma:", + "Días", + "Horas", + "Minutos", + "Segundos", + "Una vez", + "Diario", + "Lun-Vie", + "Cara", + "Cruz", + }}; + + constexpr std::array translations {{ + english, + spanish, + english, + english, + english, + english, + english, + english, + english, + english, + english, + }}; + + static_assert(english.size() == StringCount); + static_assert(spanish.size() == StringCount); + static_assert(translations.size() == LanguageCount); + + const char* TranslatePortuguese(StringId id) { + switch (id) { + case StringId::Settings: + return "Configuracoes"; + case StringId::Display: + return "Tela"; + case StringId::WakeUp: + return "Despertar"; + case StringId::TimeFormat: + return "Formato hora"; + case StringId::WatchFace: + return "Mostrador"; + case StringId::Steps: + return "Passos"; + case StringId::HeartRate: + return "Pulso"; + case StringId::DateTime: + return "Data e hora"; + case StringId::Weather: + return "Tempo"; + case StringId::Battery: + return "Bateria"; + case StringId::Chimes: + return "Sinos"; + case StringId::About: + return "Sobre"; + case StringId::Language: + return "Idioma"; + case StringId::English: + return "Ingles"; + case StringId::Spanish: + return "Espanhol"; + case StringId::Portuguese: + return "Portugues"; + case StringId::Russian: + return "Russo"; + case StringId::French: + return "Frances"; + case StringId::German: + return "Alemao"; + case StringId::Italian: + return "Italiano"; + case StringId::Turkish: + return "Turco"; + case StringId::Polish: + return "Polones"; + case StringId::Catalan: + return "Catalao"; + case StringId::Basque: + return "Euskera"; + case StringId::Back: + return "Voltar"; + case StringId::On: + return "Lig"; + case StringId::Off: + return "Desl"; + case StringId::Enabled: + return "Ativado"; + case StringId::Disabled: + return "Desativado"; + case StringId::Start: + return "Iniciar"; + case StringId::Stop: + return "Parar"; + case StringId::Pause: + return "Pausa"; + case StringId::Reset: + return "Redefinir"; + case StringId::Set: + return "Definir"; + case StringId::Charging: + return "Carregando"; + case StringId::FullyCharged: + return "Carga total"; + case StringId::Discharging: + return "Descarregando"; + case StringId::BatteryLow: + return "Bateria fraca"; + case StringId::BatteryCritical: + return "Bateria critica"; + case StringId::ReadingBattery: + return "Lendo bateria"; + case StringId::Volts: + return "volts"; + case StringId::DailyStepsGoal: + return "Meta diaria"; + case StringId::Goal: + return "Meta"; + case StringId::Trip: + return "Percurso"; + case StringId::Yesterday: + return "Ontem"; + case StringId::HeartRateBpm: + return "Pulso BPM"; + case StringId::NotEnoughData: + return "Dados insuficientes,\naguarde..."; + case StringId::NoTouchDetected: + return "Sem contato"; + case StringId::Measuring: + return "Medindo..."; + case StringId::Stopped: + return "Parado"; + case StringId::BackgroundInterval: + return "Interv. fundo"; + case StringId::Continuous: + return "Continuo"; + case StringId::DisplayTimeout: + return "Tempo da tela"; + case StringId::AlwaysOn: + return "Sempre ativa"; + case StringId::SetCurrentTime: + return "Ajustar hora"; + case StringId::SetCurrentDate: + return "Ajustar data"; + case StringId::Metric: + return "Metrico"; + case StringId::Imperial: + return "Imperial"; + case StringId::EveryHour: + return "Cada hora"; + case StringId::Every30Minutes: + return "Cada 30 min"; + case StringId::TillReboot: + return "Ate reiniciar"; + case StringId::FirmwareAndFiles: + return "Firmware e arquivos"; + case StringId::ClearSky: + return "Ceu limpo"; + case StringId::FewClouds: + return "Poucas nuvens"; + case StringId::ScatteredClouds: + return "Nuvens dispersas"; + case StringId::BrokenClouds: + return "Muito nublado"; + case StringId::ShowerRain: + return "Pancadas"; + case StringId::Rain: + return "Chuva"; + case StringId::Thunderstorm: + return "Tempestade"; + case StringId::Snow: + return "Neve"; + case StringId::Mist: + return "Neblina"; + case StringId::Clear: + return "Limpo"; + case StringId::Cloudy: + return "Nublado"; + case StringId::Showers: + return "Pancadas"; + case StringId::Thunder: + return "Trovao"; + case StringId::Notification: + return "Notificacao"; + case StringId::IncomingCallFrom: + return "Chamada de"; + case StringId::Notifications: + return "Notificacoes"; + case StringId::NoNotifications: + return "Sem notificacoes"; + case StringId::TooLarge: + return "Grande demais"; + case StringId::ZeroDivision: + return "Divisao por zero"; + case StringId::Warning: + return "Aviso"; + case StringId::TouchControllerError: + return "Erro no toque detectado."; + case StringId::WarrantyTip: + return "Se houver problemas e estiver na garantia, contacte o vendedor."; + case StringId::Proceed: + return "Continuar"; + case StringId::Version: + return "Versao"; + case StringId::FirmwareValidated: + return "Este firmware foi\n#00ff00 validado#"; + case StringId::FirmwareRollbackWarning: + return "Reiniciar vai\nvoltar ao ultimo\nfirmware validado"; + case StringId::Validate: + return "Validar"; + case StringId::Rollback: + return "Reverter"; + case StringId::FirmwareUpdate: + return "Atualizacao firmware"; + case StringId::Waiting: + return "Aguardando..."; + case StringId::WakeSensitivity: + return "Sensib. despertar"; + case StringId::Calibrate: + return "Calibrar"; + case StringId::Shake: + return "Agite!"; + case StringId::Ready: + return "Pronto!"; + case StringId::Connected: + return "Conectado"; + case StringId::Disconnected: + return "Desconectado"; + case StringId::ImageOk: + return "Imagem OK!"; + case StringId::Error: + return "Erro!"; + case StringId::YouHaveMail: + return "Voce tem\ncorreio!"; + case StringId::Navigation: + return "Navegacao"; + case StringId::Score: + return "Pontos"; + case StringId::AlarmNotSet: + return "Alarme\nnao esta\nativo."; + case StringId::TimeToAlarm: + return "Tempo para\no alarme:"; + case StringId::Days: + return "Dias"; + case StringId::Hours: + return "Horas"; + case StringId::Minutes: + return "Minutos"; + case StringId::Seconds: + return "Segundos"; + case StringId::Once: + return "Uma vez"; + case StringId::Daily: + return "Diario"; + case StringId::Weekdays: + return "Seg-Sex"; + case StringId::Heads: + return "Cara"; + case StringId::Tails: + return "Coroa"; + default: + return nullptr; + } + } + + const char* TranslateRussian(StringId id) { + switch (id) { + case StringId::Settings: + return "Настройки"; + case StringId::Display: + return "Экран"; + case StringId::WakeUp: + return "Пробуждение"; + case StringId::TimeFormat: + return "Формат времени"; + case StringId::WatchFace: + return "Циферблат"; + case StringId::Steps: + return "Шаги"; + case StringId::HeartRate: + return "Пульс"; + case StringId::DateTime: + return "Дата и время"; + case StringId::Weather: + return "Погода"; + case StringId::Battery: + return "Батарея"; + case StringId::Chimes: + return "Сигналы"; + case StringId::About: + return "О системе"; + case StringId::Language: + return "Язык"; + case StringId::English: + return "Английский"; + case StringId::Spanish: + return "Испанский"; + case StringId::Portuguese: + return "Португальский"; + case StringId::Russian: + return "Русский"; + case StringId::French: + return "Французский"; + case StringId::German: + return "Немецкий"; + case StringId::Italian: + return "Итальянский"; + case StringId::Turkish: + return "Турецкий"; + case StringId::Polish: + return "Польский"; + case StringId::Catalan: + return "Каталанский"; + case StringId::Basque: + return "Баскский"; + case StringId::Back: + return "Назад"; + case StringId::On: + return "Вкл"; + case StringId::Off: + return "Выкл"; + case StringId::Enabled: + return "Включено"; + case StringId::Disabled: + return "Выключено"; + case StringId::Start: + return "Пуск"; + case StringId::Stop: + return "Стоп"; + case StringId::Pause: + return "Пауза"; + case StringId::Reset: + return "Сброс"; + case StringId::Set: + return "Уст."; + case StringId::Charging: + return "Зарядка"; + case StringId::FullyCharged: + return "Заряжено"; + case StringId::Discharging: + return "Разрядка"; + case StringId::BatteryLow: + return "Низкий заряд"; + case StringId::BatteryCritical: + return "Критичный заряд"; + case StringId::ReadingBattery: + return "Чтение батареи"; + case StringId::Volts: + return "вольт"; + case StringId::DailyStepsGoal: + return "Цель шагов"; + case StringId::Goal: + return "Цель"; + case StringId::Trip: + return "Сессия"; + case StringId::Yesterday: + return "Вчера"; + case StringId::HeartRateBpm: + return "Пульс уд/м"; + case StringId::NotEnoughData: + return "Недостаточно данных,\nподождите..."; + case StringId::NoTouchDetected: + return "Нет касания"; + case StringId::Measuring: + return "Измерение..."; + case StringId::Stopped: + return "Остановлено"; + case StringId::BackgroundInterval: + return "Интервал фона"; + case StringId::Continuous: + return "Непрерывно"; + case StringId::DisplayTimeout: + return "Таймаут экрана"; + case StringId::AlwaysOn: + return "Всегда вкл."; + case StringId::SetCurrentTime: + return "Установить время"; + case StringId::SetCurrentDate: + return "Установить дату"; + case StringId::Metric: + return "Метрич."; + case StringId::Imperial: + return "Имперск."; + case StringId::EveryHour: + return "Каждый час"; + case StringId::Every30Minutes: + return "Каждые 30 мин"; + case StringId::TillReboot: + return "До перезагр."; + case StringId::FirmwareAndFiles: + return "Firmware и файлы"; + case StringId::ClearSky: + return "Ясно"; + case StringId::FewClouds: + return "Мало облаков"; + case StringId::ScatteredClouds: + return "Облака"; + case StringId::BrokenClouds: + return "Пасмурно"; + case StringId::ShowerRain: + return "Ливень"; + case StringId::Rain: + return "Дождь"; + case StringId::Thunderstorm: + return "Гроза"; + case StringId::Snow: + return "Снег"; + case StringId::Mist: + return "Туман"; + case StringId::Clear: + return "Ясно"; + case StringId::Cloudy: + return "Облачно"; + case StringId::Showers: + return "Ливни"; + case StringId::Thunder: + return "Гром"; + case StringId::Notification: + return "Уведомление"; + case StringId::IncomingCallFrom: + return "Входящий вызов от"; + case StringId::Notifications: + return "Уведомления"; + case StringId::NoNotifications: + return "Нет уведомлений"; + case StringId::TooLarge: + return "Слишком большое"; + case StringId::ZeroDivision: + return "Деление на ноль"; + case StringId::Warning: + return "Внимание"; + case StringId::TouchControllerError: + return "Ошибка сенсора."; + case StringId::WarrantyTip: + return "Если часы на гарантии и есть проблемы, обратитесь к продавцу."; + case StringId::Proceed: + return "Продолжить"; + case StringId::Version: + return "Версия"; + case StringId::FirmwareValidated: + return "Эта версия\n#00ff00 подтверждена#"; + case StringId::FirmwareRollbackWarning: + return "Любая перезагрузка\nвернет последнюю\nподтвержденную версию"; + case StringId::Validate: + return "Подтв."; + case StringId::Rollback: + return "Откат"; + case StringId::FirmwareUpdate: + return "Обновление ПО"; + case StringId::Waiting: + return "Ожидание..."; + case StringId::WakeSensitivity: + return "Чувств. жеста"; + case StringId::Calibrate: + return "Калибр."; + case StringId::Shake: + return "Встряхните!"; + case StringId::Ready: + return "Готово!"; + case StringId::Connected: + return "Подключено"; + case StringId::Disconnected: + return "Отключено"; + case StringId::ImageOk: + return "Образ OK!"; + case StringId::Error: + return "Ошибка!"; + case StringId::YouHaveMail: + return "У вас\nпочта!"; + case StringId::Navigation: + return "Навигация"; + case StringId::Score: + return "Счет"; + case StringId::AlarmNotSet: + return "Будильник\nне\nустановлен."; + case StringId::TimeToAlarm: + return "До сигнала:"; + case StringId::Days: + return "Дней"; + case StringId::Hours: + return "Часов"; + case StringId::Minutes: + return "Минут"; + case StringId::Seconds: + return "Секунд"; + case StringId::Once: + return "Один раз"; + case StringId::Daily: + return "Ежедневно"; + case StringId::Weekdays: + return "Пн-Пт"; + case StringId::Heads: + return "Орел"; + case StringId::Tails: + return "Решка"; + default: + return nullptr; + } + } + + const char* TranslateFrench(StringId id) { + switch (id) { + case StringId::Settings: + return "Parametres"; + case StringId::Display: + return "Affichage"; + case StringId::WakeUp: + return "Reveil"; + case StringId::TimeFormat: + return "Format heure"; + case StringId::WatchFace: + return "Cadran"; + case StringId::Steps: + return "Pas"; + case StringId::HeartRate: + return "Frequence"; + case StringId::DateTime: + return "Date et heure"; + case StringId::Weather: + return "Meteo"; + case StringId::Battery: + return "Batterie"; + case StringId::Chimes: + return "Carillon"; + case StringId::About: + return "A propos"; + case StringId::Language: + return "Langue"; + case StringId::English: + return "Anglais"; + case StringId::Spanish: + return "Espagnol"; + case StringId::Portuguese: + return "Portugais"; + case StringId::Russian: + return "Russe"; + case StringId::French: + return "Francais"; + case StringId::German: + return "Allemand"; + case StringId::Italian: + return "Italien"; + case StringId::Turkish: + return "Turc"; + case StringId::Polish: + return "Polonais"; + case StringId::Catalan: + return "Catalan"; + case StringId::Basque: + return "Basque"; + case StringId::Back: + return "Retour"; + case StringId::On: + return "Marche"; + case StringId::Off: + return "Arret"; + case StringId::Enabled: + return "Active"; + case StringId::Disabled: + return "Desactive"; + case StringId::Start: + return "Demarrer"; + case StringId::Stop: + return "Arreter"; + case StringId::Pause: + return "Pause"; + case StringId::Reset: + return "Reinit."; + case StringId::Set: + return "Regler"; + case StringId::Charging: + return "Charge"; + case StringId::FullyCharged: + return "Chargee"; + case StringId::Discharging: + return "Decharge"; + case StringId::BatteryLow: + return "Batterie faible"; + case StringId::BatteryCritical: + return "Batterie critique"; + case StringId::ReadingBattery: + return "Lecture batterie"; + case StringId::Volts: + return "volts"; + case StringId::DailyStepsGoal: + return "Objectif jour"; + case StringId::Goal: + return "Objectif"; + case StringId::Trip: + return "Trajet"; + case StringId::Yesterday: + return "Hier"; + case StringId::HeartRateBpm: + return "BPM"; + case StringId::NotEnoughData: + return "Donnees insuff.,\npatientez..."; + case StringId::NoTouchDetected: + return "Aucun contact"; + case StringId::Measuring: + return "Mesure..."; + case StringId::Stopped: + return "Arrete"; + case StringId::BackgroundInterval: + return "Interv. fond"; + case StringId::Continuous: + return "Continu"; + case StringId::DisplayTimeout: + return "Veille ecran"; + case StringId::AlwaysOn: + return "Toujours actif"; + case StringId::SetCurrentTime: + return "Regler heure"; + case StringId::SetCurrentDate: + return "Regler date"; + case StringId::Metric: + return "Metrique"; + case StringId::Imperial: + return "Imperial"; + case StringId::EveryHour: + return "Chaque heure"; + case StringId::Every30Minutes: + return "Toutes les 30 min"; + case StringId::TillReboot: + return "Jusqu'au redem."; + case StringId::FirmwareAndFiles: + return "Firmware et fichiers"; + case StringId::ClearSky: + return "Ciel degage"; + case StringId::FewClouds: + return "Peu nuageux"; + case StringId::ScatteredClouds: + return "Nuages epars"; + case StringId::BrokenClouds: + return "Tres nuageux"; + case StringId::ShowerRain: + return "Averses"; + case StringId::Rain: + return "Pluie"; + case StringId::Thunderstorm: + return "Orage"; + case StringId::Snow: + return "Neige"; + case StringId::Mist: + return "Brouillard"; + case StringId::Clear: + return "Clair"; + case StringId::Cloudy: + return "Nuageux"; + case StringId::Showers: + return "Averses"; + case StringId::Thunder: + return "Tonnerre"; + case StringId::Notification: + return "Notification"; + case StringId::IncomingCallFrom: + return "Appel de"; + case StringId::Notifications: + return "Notifications"; + case StringId::NoNotifications: + return "Aucune notification"; + case StringId::TooLarge: + return "Trop grand"; + case StringId::ZeroDivision: + return "Division par zero"; + case StringId::Warning: + return "Alerte"; + case StringId::TouchControllerError: + return "Erreur tactile detectee."; + case StringId::WarrantyTip: + return "En cas de probleme sous garantie, contactez le vendeur."; + case StringId::Proceed: + return "Continuer"; + case StringId::Version: + return "Version"; + case StringId::FirmwareValidated: + return "Ce firmware a ete\n#00ff00 valide#"; + case StringId::FirmwareRollbackWarning: + return "Tout redemarrage\nrestaurera le dernier\nfirmware valide"; + case StringId::Validate: + return "Valider"; + case StringId::Rollback: + return "Restaurer"; + case StringId::FirmwareUpdate: + return "Mise a jour FW"; + case StringId::Waiting: + return "Attente..."; + case StringId::WakeSensitivity: + return "Sensib. reveil"; + case StringId::Calibrate: + return "Calibrer"; + case StringId::Shake: + return "Secouez!"; + case StringId::Ready: + return "Pret!"; + case StringId::Connected: + return "Connecte"; + case StringId::Disconnected: + return "Deconnecte"; + case StringId::ImageOk: + return "Image OK!"; + case StringId::Error: + return "Erreur!"; + case StringId::YouHaveMail: + return "Vous avez\ndu courrier!"; + case StringId::Navigation: + return "Navigation"; + case StringId::Score: + return "Score"; + case StringId::AlarmNotSet: + return "Alarme\nnon\nreglee."; + case StringId::TimeToAlarm: + return "Temps avant\nalarme :"; + case StringId::Days: + return "Jours"; + case StringId::Hours: + return "Heures"; + case StringId::Minutes: + return "Minutes"; + case StringId::Seconds: + return "Secondes"; + case StringId::Once: + return "Une fois"; + case StringId::Daily: + return "Quotidien"; + case StringId::Weekdays: + return "Lun-Ven"; + case StringId::Heads: + return "Pile"; + case StringId::Tails: + return "Face"; + default: + return nullptr; + } + } + + const char* TranslateGerman(StringId id) { + switch (id) { + case StringId::Settings: + return "Einstellungen"; + case StringId::Display: + return "Anzeige"; + case StringId::WakeUp: + return "Aufwecken"; + case StringId::TimeFormat: + return "Zeitformat"; + case StringId::WatchFace: + return "Zifferblatt"; + case StringId::Steps: + return "Schritte"; + case StringId::HeartRate: + return "Puls"; + case StringId::DateTime: + return "Datum und Zeit"; + case StringId::Weather: + return "Wetter"; + case StringId::Battery: + return "Akku"; + case StringId::Chimes: + return "Signale"; + case StringId::About: + return "Info"; + case StringId::Language: + return "Sprache"; + case StringId::English: + return "Englisch"; + case StringId::Spanish: + return "Spanisch"; + case StringId::Portuguese: + return "Portugiesisch"; + case StringId::Russian: + return "Russisch"; + case StringId::French: + return "Franzosisch"; + case StringId::German: + return "Deutsch"; + case StringId::Italian: + return "Italienisch"; + case StringId::Turkish: + return "Turkisch"; + case StringId::Polish: + return "Polnisch"; + case StringId::Catalan: + return "Katalanisch"; + case StringId::Basque: + return "Baskisch"; + case StringId::Back: + return "Zuruck"; + case StringId::On: + return "An"; + case StringId::Off: + return "Aus"; + case StringId::Enabled: + return "Aktiv"; + case StringId::Disabled: + return "Inaktiv"; + case StringId::Start: + return "Start"; + case StringId::Stop: + return "Stop"; + case StringId::Pause: + return "Pause"; + case StringId::Reset: + return "Reset"; + case StringId::Set: + return "Setzen"; + case StringId::Charging: + return "Laden"; + case StringId::FullyCharged: + return "Voll geladen"; + case StringId::Discharging: + return "Entladen"; + case StringId::BatteryLow: + return "Akku schwach"; + case StringId::BatteryCritical: + return "Akku kritisch"; + case StringId::ReadingBattery: + return "Akku lesen"; + case StringId::Volts: + return "Volt"; + case StringId::DailyStepsGoal: + return "Tagesziel"; + case StringId::Goal: + return "Ziel"; + case StringId::Trip: + return "Strecke"; + case StringId::Yesterday: + return "Gestern"; + case StringId::HeartRateBpm: + return "Puls BPM"; + case StringId::NotEnoughData: + return "Zu wenig Daten,\nbitte warten..."; + case StringId::NoTouchDetected: + return "Kein Kontakt"; + case StringId::Measuring: + return "Messe..."; + case StringId::Stopped: + return "Gestoppt"; + case StringId::BackgroundInterval: + return "Hintergr. Interv."; + case StringId::Continuous: + return "Dauernd"; + case StringId::DisplayTimeout: + return "Bildschirmzeit"; + case StringId::AlwaysOn: + return "Immer an"; + case StringId::SetCurrentTime: + return "Zeit setzen"; + case StringId::SetCurrentDate: + return "Datum setzen"; + case StringId::Metric: + return "Metrisch"; + case StringId::Imperial: + return "Imperial"; + case StringId::EveryHour: + return "Jede Stunde"; + case StringId::Every30Minutes: + return "Alle 30 Min"; + case StringId::TillReboot: + return "Bis Neustart"; + case StringId::FirmwareAndFiles: + return "Firmware und Dateien"; + case StringId::ClearSky: + return "Klarer Himmel"; + case StringId::FewClouds: + return "Wenige Wolken"; + case StringId::ScatteredClouds: + return "Aufgelockert"; + case StringId::BrokenClouds: + return "Stark bewolkt"; + case StringId::ShowerRain: + return "Schauer"; + case StringId::Rain: + return "Regen"; + case StringId::Thunderstorm: + return "Gewitter"; + case StringId::Snow: + return "Schnee"; + case StringId::Mist: + return "Nebel"; + case StringId::Clear: + return "Klar"; + case StringId::Cloudy: + return "Wolkig"; + case StringId::Showers: + return "Schauer"; + case StringId::Thunder: + return "Donner"; + case StringId::Notification: + return "Benachrichtigung"; + case StringId::IncomingCallFrom: + return "Anruf von"; + case StringId::Notifications: + return "Benachrichtigungen"; + case StringId::NoNotifications: + return "Keine Benachrichtigungen"; + case StringId::TooLarge: + return "Zu gross"; + case StringId::ZeroDivision: + return "Division durch null"; + case StringId::Warning: + return "Warnung"; + case StringId::TouchControllerError: + return "Touch-Fehler erkannt."; + case StringId::WarrantyTip: + return "Bei Problemen in der Garantie den Verkaufer kontaktieren."; + case StringId::Proceed: + return "Fortfahren"; + case StringId::Version: + return "Version"; + case StringId::FirmwareValidated: + return "Diese Firmware ist\n#00ff00 validiert#"; + case StringId::FirmwareRollbackWarning: + return "Jeder Neustart\nsetzt die letzte\nvalidierte Firmware zuruck"; + case StringId::Validate: + return "Validieren"; + case StringId::Rollback: + return "Zuruck"; + case StringId::FirmwareUpdate: + return "Firmware-Update"; + case StringId::Waiting: + return "Warten..."; + case StringId::WakeSensitivity: + return "Weck-Sensib."; + case StringId::Calibrate: + return "Kalibrieren"; + case StringId::Shake: + return "Schutteln!"; + case StringId::Ready: + return "Bereit!"; + case StringId::Connected: + return "Verbunden"; + case StringId::Disconnected: + return "Getrennt"; + case StringId::ImageOk: + return "Image OK!"; + case StringId::Error: + return "Fehler!"; + case StringId::YouHaveMail: + return "Sie haben\nPost!"; + case StringId::Navigation: + return "Navigation"; + case StringId::Score: + return "Punkte"; + case StringId::AlarmNotSet: + return "Alarm\nist nicht\ngesetzt."; + case StringId::TimeToAlarm: + return "Zeit bis\nAlarm:"; + case StringId::Days: + return "Tage"; + case StringId::Hours: + return "Stunden"; + case StringId::Minutes: + return "Minuten"; + case StringId::Seconds: + return "Sekunden"; + case StringId::Once: + return "Einmal"; + case StringId::Daily: + return "Taglich"; + case StringId::Weekdays: + return "Mo-Fr"; + case StringId::Heads: + return "Kopf"; + case StringId::Tails: + return "Zahl"; + default: + return nullptr; + } + } + + const char* TranslateItalian(StringId id) { + switch (id) { + case StringId::Settings: + return "Impostazioni"; + case StringId::Display: + return "Schermo"; + case StringId::WakeUp: + return "Risveglio"; + case StringId::TimeFormat: + return "Formato ora"; + case StringId::WatchFace: + return "Quadrante"; + case StringId::Steps: + return "Passi"; + case StringId::HeartRate: + return "Battito"; + case StringId::DateTime: + return "Data e ora"; + case StringId::Weather: + return "Meteo"; + case StringId::Battery: + return "Batteria"; + case StringId::Chimes: + return "Rintocchi"; + case StringId::About: + return "Info"; + case StringId::Language: + return "Lingua"; + case StringId::English: + return "Inglese"; + case StringId::Spanish: + return "Spagnolo"; + case StringId::Portuguese: + return "Portoghese"; + case StringId::Russian: + return "Russo"; + case StringId::French: + return "Francese"; + case StringId::German: + return "Tedesco"; + case StringId::Italian: + return "Italiano"; + case StringId::Turkish: + return "Turco"; + case StringId::Polish: + return "Polacco"; + case StringId::Catalan: + return "Catalano"; + case StringId::Basque: + return "Basco"; + case StringId::Back: + return "Indietro"; + case StringId::On: + return "Acc."; + case StringId::Off: + return "Spento"; + case StringId::Enabled: + return "Attivo"; + case StringId::Disabled: + return "Disattivo"; + case StringId::Start: + return "Avvia"; + case StringId::Stop: + return "Ferma"; + case StringId::Pause: + return "Pausa"; + case StringId::Reset: + return "Azzera"; + case StringId::Set: + return "Imposta"; + case StringId::Charging: + return "In carica"; + case StringId::FullyCharged: + return "Carica completa"; + case StringId::Discharging: + return "Scarica"; + case StringId::BatteryLow: + return "Batteria bassa"; + case StringId::BatteryCritical: + return "Batteria critica"; + case StringId::ReadingBattery: + return "Lettura batteria"; + case StringId::Volts: + return "volt"; + case StringId::DailyStepsGoal: + return "Obiettivo giorn."; + case StringId::Goal: + return "Obiettivo"; + case StringId::Trip: + return "Tragitto"; + case StringId::Yesterday: + return "Ieri"; + case StringId::HeartRateBpm: + return "Battiti/min"; + case StringId::NotEnoughData: + return "Dati insufficienti,\nattendi..."; + case StringId::NoTouchDetected: + return "Nessun contatto"; + case StringId::Measuring: + return "Misura..."; + case StringId::Stopped: + return "Fermato"; + case StringId::BackgroundInterval: + return "Intervallo bg"; + case StringId::Continuous: + return "Continuo"; + case StringId::DisplayTimeout: + return "Timeout schermo"; + case StringId::AlwaysOn: + return "Sempre acceso"; + case StringId::SetCurrentTime: + return "Imposta ora"; + case StringId::SetCurrentDate: + return "Imposta data"; + case StringId::Metric: + return "Metrico"; + case StringId::Imperial: + return "Imperiale"; + case StringId::EveryHour: + return "Ogni ora"; + case StringId::Every30Minutes: + return "Ogni 30 min"; + case StringId::TillReboot: + return "Fino al riavvio"; + case StringId::FirmwareAndFiles: + return "Firmware e file"; + case StringId::ClearSky: + return "Sereno"; + case StringId::FewClouds: + return "Poche nuvole"; + case StringId::ScatteredClouds: + return "Nuvole sparse"; + case StringId::BrokenClouds: + return "Molto nuvoloso"; + case StringId::ShowerRain: + return "Rovesci"; + case StringId::Rain: + return "Pioggia"; + case StringId::Thunderstorm: + return "Temporale"; + case StringId::Snow: + return "Neve"; + case StringId::Mist: + return "Nebbia"; + case StringId::Clear: + return "Sereno"; + case StringId::Cloudy: + return "Nuvoloso"; + case StringId::Showers: + return "Rovesci"; + case StringId::Thunder: + return "Tuono"; + case StringId::Notification: + return "Notifica"; + case StringId::IncomingCallFrom: + return "Chiamata da"; + case StringId::Notifications: + return "Notifiche"; + case StringId::NoNotifications: + return "Nessuna notifica"; + case StringId::TooLarge: + return "Troppo grande"; + case StringId::ZeroDivision: + return "Divisione per zero"; + case StringId::Warning: + return "Avviso"; + case StringId::TouchControllerError: + return "Errore touch rilevato."; + case StringId::WarrantyTip: + return "Se ci sono problemi in garanzia, contatta il venditore."; + case StringId::Proceed: + return "Continua"; + case StringId::Version: + return "Versione"; + case StringId::FirmwareValidated: + return "Questo firmware e\n#00ff00 validato#"; + case StringId::FirmwareRollbackWarning: + return "Qualsiasi riavvio\nripristinera l'ultimo\nfirmware validato"; + case StringId::Validate: + return "Valida"; + case StringId::Rollback: + return "Ripristina"; + case StringId::FirmwareUpdate: + return "Agg. firmware"; + case StringId::Waiting: + return "Attesa..."; + case StringId::WakeSensitivity: + return "Sensib. risveglio"; + case StringId::Calibrate: + return "Calibra"; + case StringId::Shake: + return "Scuoti!"; + case StringId::Ready: + return "Pronto!"; + case StringId::Connected: + return "Connesso"; + case StringId::Disconnected: + return "Disconnesso"; + case StringId::ImageOk: + return "Immagine OK!"; + case StringId::Error: + return "Errore!"; + case StringId::YouHaveMail: + return "Hai\nposta!"; + case StringId::Navigation: + return "Navigazione"; + case StringId::Score: + return "Punteggio"; + case StringId::AlarmNotSet: + return "Sveglia\nnon\nimpostata."; + case StringId::TimeToAlarm: + return "Tempo alla\nsveglia:"; + case StringId::Days: + return "Giorni"; + case StringId::Hours: + return "Ore"; + case StringId::Minutes: + return "Minuti"; + case StringId::Seconds: + return "Secondi"; + case StringId::Once: + return "Una volta"; + case StringId::Daily: + return "Ogni giorno"; + case StringId::Weekdays: + return "Lun-Ven"; + case StringId::Heads: + return "Testa"; + case StringId::Tails: + return "Croce"; + default: + return nullptr; + } + } + + const char* TranslateTurkish(StringId id) { + switch (id) { + case StringId::Settings: + return "Ayarlar"; + case StringId::Display: + return "Ekran"; + case StringId::WakeUp: + return "Uyanma"; + case StringId::TimeFormat: + return "Saat bicimi"; + case StringId::WatchFace: + return "Saat yuzu"; + case StringId::Steps: + return "Adimlar"; + case StringId::HeartRate: + return "Nabiz"; + case StringId::DateTime: + return "Tarih ve saat"; + case StringId::Weather: + return "Hava"; + case StringId::Battery: + return "Pil"; + case StringId::Chimes: + return "Saat uyarisi"; + case StringId::About: + return "Hakkinda"; + case StringId::Language: + return "Dil"; + case StringId::English: + return "Ingilizce"; + case StringId::Spanish: + return "Ispanyolca"; + case StringId::Portuguese: + return "Portekizce"; + case StringId::Russian: + return "Rusca"; + case StringId::French: + return "Fransizca"; + case StringId::German: + return "Almanca"; + case StringId::Italian: + return "Italyanca"; + case StringId::Turkish: + return "Turkce"; + case StringId::Polish: + return "Lehce"; + case StringId::Catalan: + return "Katalanca"; + case StringId::Basque: + return "Baskca"; + case StringId::Back: + return "Geri"; + case StringId::On: + return "Acik"; + case StringId::Off: + return "Kapali"; + case StringId::Enabled: + return "Etkin"; + case StringId::Disabled: + return "Devre disi"; + case StringId::Start: + return "Baslat"; + case StringId::Stop: + return "Durdur"; + case StringId::Pause: + return "Duraklat"; + case StringId::Reset: + return "Sifirla"; + case StringId::Set: + return "Ayarla"; + case StringId::Charging: + return "Sarj oluyor"; + case StringId::FullyCharged: + return "Tam dolu"; + case StringId::Discharging: + return "Desarj oluyor"; + case StringId::BatteryLow: + return "Pil dusuk"; + case StringId::BatteryCritical: + return "Pil kritik"; + case StringId::ReadingBattery: + return "Pil okunuyor"; + case StringId::Volts: + return "volt"; + case StringId::DailyStepsGoal: + return "Gunluk hedef"; + case StringId::Goal: + return "Hedef"; + case StringId::Trip: + return "Tur"; + case StringId::Yesterday: + return "Dun"; + case StringId::HeartRateBpm: + return "Nabiz BPM"; + case StringId::NotEnoughData: + return "Yetersiz veri,\nlutfen bekleyin..."; + case StringId::NoTouchDetected: + return "Dokunus yok"; + case StringId::Measuring: + return "Olculuyor..."; + case StringId::Stopped: + return "Durdu"; + case StringId::BackgroundInterval: + return "Arka plan araligi"; + case StringId::Continuous: + return "Surekli"; + case StringId::DisplayTimeout: + return "Ekran suresi"; + case StringId::AlwaysOn: + return "Hep acik"; + case StringId::SetCurrentTime: + return "Saati ayarla"; + case StringId::SetCurrentDate: + return "Tarihi ayarla"; + case StringId::Metric: + return "Metrik"; + case StringId::Imperial: + return "Imperyal"; + case StringId::EveryHour: + return "Her saat"; + case StringId::Every30Minutes: + return "Her 30 dk"; + case StringId::TillReboot: + return "Yeniden baslatana dek"; + case StringId::FirmwareAndFiles: + return "Firmware ve dosyalar"; + case StringId::ClearSky: + return "Acik"; + case StringId::FewClouds: + return "Az bulutlu"; + case StringId::ScatteredClouds: + return "Parcali bulutlu"; + case StringId::BrokenClouds: + return "Cok bulutlu"; + case StringId::ShowerRain: + return "Saganak"; + case StringId::Rain: + return "Yagmur"; + case StringId::Thunderstorm: + return "Firtina"; + case StringId::Snow: + return "Kar"; + case StringId::Mist: + return "Sis"; + case StringId::Clear: + return "Acik"; + case StringId::Cloudy: + return "Bulutlu"; + case StringId::Showers: + return "Saganak"; + case StringId::Thunder: + return "Gok gurultusu"; + case StringId::Notification: + return "Bildirim"; + case StringId::IncomingCallFrom: + return "Arayan"; + case StringId::Notifications: + return "Bildirimler"; + case StringId::NoNotifications: + return "Bildirim yok"; + case StringId::TooLarge: + return "Cok buyuk"; + case StringId::ZeroDivision: + return "Sifira bolme"; + case StringId::Warning: + return "Uyari"; + case StringId::TouchControllerError: + return "Dokunmatik hatasi algilandi."; + case StringId::WarrantyTip: + return "Sorun olursa ve garanti varsa saticiyla iletisime gecin."; + case StringId::Proceed: + return "Devam"; + case StringId::Version: + return "Surum"; + case StringId::ShortRef: + return "Kisa ref"; + case StringId::FirmwareValidated: + return "Bu firmware\n#00ff00 dogrulandi#"; + case StringId::FirmwareRollbackWarning: + return "Her yeniden baslatma\nson dogrulanan\nfirmware'e doner"; + case StringId::Validate: + return "Dogrula"; + case StringId::Rollback: + return "Geri al"; + case StringId::FirmwareUpdate: + return "Firmware guncelleme"; + case StringId::Waiting: + return "Bekleniyor..."; + case StringId::WakeSensitivity: + return "Uyanma hass."; + case StringId::Calibrate: + return "Kalibre et"; + case StringId::Shake: + return "Salla!"; + case StringId::Ready: + return "Hazir!"; + case StringId::Connected: + return "Bagli"; + case StringId::Disconnected: + return "Bagli degil"; + case StringId::ImageOk: + return "Imge tamam!"; + case StringId::Error: + return "Hata!"; + case StringId::YouHaveMail: + return "Yeni\nmesajin var!"; + case StringId::Navigation: + return "Navigasyon"; + case StringId::Score: + return "Skor"; + case StringId::AlarmNotSet: + return "Alarm\nkurulu\ndegil."; + case StringId::TimeToAlarm: + return "Alarma\nkalan:"; + case StringId::Days: + return "Gun"; + case StringId::Hours: + return "Saat"; + case StringId::Minutes: + return "Dakika"; + case StringId::Seconds: + return "Saniye"; + case StringId::Once: + return "Bir kez"; + case StringId::Daily: + return "Her gun"; + case StringId::Weekdays: + return "Pzt-Cum"; + case StringId::Heads: + return "Yazi"; + case StringId::Tails: + return "Tura"; + default: + return nullptr; + } + } + + const char* TranslatePolish(StringId id) { + switch (id) { + case StringId::Settings: + return "Ustawienia"; + case StringId::Display: + return "Ekran"; + case StringId::WakeUp: + return "Wybudzanie"; + case StringId::TimeFormat: + return "Format czasu"; + case StringId::WatchFace: + return "Tarcza"; + case StringId::Steps: + return "Kroki"; + case StringId::HeartRate: + return "Tetno"; + case StringId::DateTime: + return "Data i czas"; + case StringId::Weather: + return "Pogoda"; + case StringId::Battery: + return "Bateria"; + case StringId::Chimes: + return "Sygnaly"; + case StringId::About: + return "O zegarku"; + case StringId::Language: + return "Jezyk"; + case StringId::English: + return "Angielski"; + case StringId::Spanish: + return "Hiszpanski"; + case StringId::Portuguese: + return "Portugalski"; + case StringId::Russian: + return "Rosyjski"; + case StringId::French: + return "Francuski"; + case StringId::German: + return "Niemiecki"; + case StringId::Italian: + return "Wloski"; + case StringId::Turkish: + return "Turecki"; + case StringId::Polish: + return "Polski"; + case StringId::Catalan: + return "Katalonski"; + case StringId::Basque: + return "Baskijski"; + case StringId::Back: + return "Wstecz"; + case StringId::On: + return "Wl."; + case StringId::Off: + return "Wyl."; + case StringId::Enabled: + return "Wlaczone"; + case StringId::Disabled: + return "Wylaczone"; + case StringId::Start: + return "Start"; + case StringId::Stop: + return "Stop"; + case StringId::Pause: + return "Pauza"; + case StringId::Reset: + return "Reset"; + case StringId::Set: + return "Ustaw"; + case StringId::Charging: + return "Ladowanie"; + case StringId::FullyCharged: + return "Naladowana"; + case StringId::Discharging: + return "Rozladowanie"; + case StringId::BatteryLow: + return "Niski poziom"; + case StringId::BatteryCritical: + return "Krytyczny poziom"; + case StringId::ReadingBattery: + return "Odczyt baterii"; + case StringId::Volts: + return "V"; + case StringId::DailyStepsGoal: + return "Cel dzienny"; + case StringId::Goal: + return "Cel"; + case StringId::Trip: + return "Sesja"; + case StringId::Yesterday: + return "Wczoraj"; + case StringId::HeartRateBpm: + return "Tetno BPM"; + case StringId::NotEnoughData: + return "Za malo danych,\nczekaj..."; + case StringId::NoTouchDetected: + return "Brak dotyku"; + case StringId::Measuring: + return "Pomiar..."; + case StringId::Stopped: + return "Zatrzymano"; + case StringId::BackgroundInterval: + return "Interwal tla"; + case StringId::Continuous: + return "Ciagle"; + case StringId::DisplayTimeout: + return "Wygaszanie"; + case StringId::AlwaysOn: + return "Zawsze wl."; + case StringId::SetCurrentTime: + return "Ustaw czas"; + case StringId::SetCurrentDate: + return "Ustaw date"; + case StringId::Metric: + return "Metryczne"; + case StringId::Imperial: + return "Imperialne"; + case StringId::EveryHour: + return "Co godzine"; + case StringId::Every30Minutes: + return "Co 30 min"; + case StringId::TillReboot: + return "Do restartu"; + case StringId::FirmwareAndFiles: + return "Firmware i pliki"; + case StringId::ClearSky: + return "Bezchmurnie"; + case StringId::FewClouds: + return "Malo chmur"; + case StringId::ScatteredClouds: + return "Chmury"; + case StringId::BrokenClouds: + return "Pochmurno"; + case StringId::ShowerRain: + return "Przelotnie"; + case StringId::Rain: + return "Deszcz"; + case StringId::Thunderstorm: + return "Burza"; + case StringId::Snow: + return "Snieg"; + case StringId::Mist: + return "Mgla"; + case StringId::Clear: + return "Pogodnie"; + case StringId::Cloudy: + return "Pochmurno"; + case StringId::Showers: + return "Przelotnie"; + case StringId::Thunder: + return "Grzmot"; + case StringId::Notification: + return "Powiadomienie"; + case StringId::IncomingCallFrom: + return "Polaczenie od"; + case StringId::Notifications: + return "Powiadomienia"; + case StringId::NoNotifications: + return "Brak powiadomien"; + case StringId::TooLarge: + return "Za duze"; + case StringId::ZeroDivision: + return "Dzielenie przez zero"; + case StringId::Warning: + return "Ostrzezenie"; + case StringId::TouchControllerError: + return "Wykryto blad dotyku."; + case StringId::WarrantyTip: + return "Jesli sa problemy i jest gwarancja, skontaktuj sie ze sprzedawca."; + case StringId::Proceed: + return "Dalej"; + case StringId::Version: + return "Wersja"; + case StringId::ShortRef: + return "Krotki ref"; + case StringId::FirmwareValidated: + return "To firmware jest\n#00ff00 zweryfikowane#"; + case StringId::FirmwareRollbackWarning: + return "Kazdy restart\nprzywroci ostatnie\nzweryfikowane firmware"; + case StringId::Validate: + return "Zweryfikuj"; + case StringId::Rollback: + return "Cofnij"; + case StringId::FirmwareUpdate: + return "Aktualizacja FW"; + case StringId::Waiting: + return "Oczekiwanie..."; + case StringId::WakeSensitivity: + return "Czulosc budz."; + case StringId::Calibrate: + return "Kalibruj"; + case StringId::Shake: + return "Potrzasnij!"; + case StringId::Ready: + return "Gotowe!"; + case StringId::Connected: + return "Polaczono"; + case StringId::Disconnected: + return "Rozlaczono"; + case StringId::ImageOk: + return "Obraz OK!"; + case StringId::Error: + return "Blad!"; + case StringId::YouHaveMail: + return "Masz\nwiadomosc!"; + case StringId::Navigation: + return "Nawigacja"; + case StringId::Score: + return "Wynik"; + case StringId::AlarmNotSet: + return "Alarm\nnie jest\nustawiony."; + case StringId::TimeToAlarm: + return "Do alarmu:"; + case StringId::Days: + return "Dni"; + case StringId::Hours: + return "Godzin"; + case StringId::Minutes: + return "Minut"; + case StringId::Seconds: + return "Sekund"; + case StringId::Once: + return "Raz"; + case StringId::Daily: + return "Codziennie"; + case StringId::Weekdays: + return "Pon-Pt"; + case StringId::Heads: + return "Orzel"; + case StringId::Tails: + return "Reszka"; + default: + return nullptr; + } + } + + const char* TranslateCatalan(StringId id) { + switch (id) { + case StringId::Settings: + return "Ajustos"; + case StringId::Display: + return "Pantalla"; + case StringId::WakeUp: + return "Activacio"; + case StringId::TimeFormat: + return "Format hora"; + case StringId::WatchFace: + return "Esfera"; + case StringId::Steps: + return "Passos"; + case StringId::HeartRate: + return "Pols"; + case StringId::DateTime: + return "Data i hora"; + case StringId::Weather: + return "Temps"; + case StringId::Battery: + return "Bateria"; + case StringId::Chimes: + return "Campanades"; + case StringId::About: + return "Quant a"; + case StringId::Language: + return "Idioma"; + case StringId::English: + return "Angles"; + case StringId::Spanish: + return "Castella"; + case StringId::Portuguese: + return "Portugues"; + case StringId::Russian: + return "Rus"; + case StringId::French: + return "Frances"; + case StringId::German: + return "Alemany"; + case StringId::Italian: + return "Italia"; + case StringId::Turkish: + return "Turc"; + case StringId::Polish: + return "Polones"; + case StringId::Catalan: + return "Catala"; + case StringId::Basque: + return "Euskara"; + case StringId::Back: + return "Enrere"; + case StringId::On: + return "Enc."; + case StringId::Off: + return "Apag."; + case StringId::Enabled: + return "Activat"; + case StringId::Disabled: + return "Desactivat"; + case StringId::Start: + return "Inicia"; + case StringId::Stop: + return "Atura"; + case StringId::Pause: + return "Pausa"; + case StringId::Reset: + return "Reinicia"; + case StringId::Set: + return "Fixa"; + case StringId::Charging: + return "Carregant"; + case StringId::FullyCharged: + return "Carrega completa"; + case StringId::Discharging: + return "Descarregant"; + case StringId::BatteryLow: + return "Bateria baixa"; + case StringId::BatteryCritical: + return "Bateria critica"; + case StringId::ReadingBattery: + return "Llegint bateria"; + case StringId::Volts: + return "volts"; + case StringId::DailyStepsGoal: + return "Objectiu diari"; + case StringId::Goal: + return "Objectiu"; + case StringId::Trip: + return "Trajecte"; + case StringId::Yesterday: + return "Ahir"; + case StringId::HeartRateBpm: + return "Pols BPM"; + case StringId::NotEnoughData: + return "Dades insuf.,\nespera..."; + case StringId::NoTouchDetected: + return "Sense contacte"; + case StringId::Measuring: + return "Mesurant..."; + case StringId::Stopped: + return "Aturat"; + case StringId::BackgroundInterval: + return "Interval fons"; + case StringId::Continuous: + return "Continu"; + case StringId::DisplayTimeout: + return "Temps pantalla"; + case StringId::AlwaysOn: + return "Sempre activa"; + case StringId::SetCurrentTime: + return "Ajusta hora"; + case StringId::SetCurrentDate: + return "Ajusta data"; + case StringId::Metric: + return "Metric"; + case StringId::Imperial: + return "Imperial"; + case StringId::EveryHour: + return "Cada hora"; + case StringId::Every30Minutes: + return "Cada 30 min"; + case StringId::TillReboot: + return "Fins reinici"; + case StringId::FirmwareAndFiles: + return "Firmware i fitxers"; + case StringId::ClearSky: + return "Cel clar"; + case StringId::FewClouds: + return "Pocs nuvols"; + case StringId::ScatteredClouds: + return "Nuvols dispersos"; + case StringId::BrokenClouds: + return "Molt ennuvolat"; + case StringId::ShowerRain: + return "Ruixats"; + case StringId::Rain: + return "Pluja"; + case StringId::Thunderstorm: + return "Tempesta"; + case StringId::Snow: + return "Neu"; + case StringId::Mist: + return "Boira"; + case StringId::Clear: + return "Clar"; + case StringId::Cloudy: + return "Ennuvolat"; + case StringId::Showers: + return "Ruixats"; + case StringId::Thunder: + return "Tro"; + case StringId::Notification: + return "Notificacio"; + case StringId::IncomingCallFrom: + return "Trucada de"; + case StringId::Notifications: + return "Notificacions"; + case StringId::NoNotifications: + return "Sense notificacions"; + case StringId::TooLarge: + return "Massa gran"; + case StringId::ZeroDivision: + return "Divisio per zero"; + case StringId::Warning: + return "Avis"; + case StringId::TouchControllerError: + return "Error tactil detectat."; + case StringId::WarrantyTip: + return "Si hi ha problemes i es en garantia, contacta amb el venedor."; + case StringId::Proceed: + return "Continua"; + case StringId::Version: + return "Versio"; + case StringId::ShortRef: + return "Ref curta"; + case StringId::FirmwareValidated: + return "Aquest firmware ha\nestat #00ff00 validat#"; + case StringId::FirmwareRollbackWarning: + return "Qualsevol reinici\nrestaurara l'ultim\nfirmware validat"; + case StringId::Validate: + return "Valida"; + case StringId::Rollback: + return "Reverteix"; + case StringId::FirmwareUpdate: + return "Actual. firmware"; + case StringId::Waiting: + return "Esperant..."; + case StringId::WakeSensitivity: + return "Sensib. despert."; + case StringId::Calibrate: + return "Calibra"; + case StringId::Shake: + return "Sacseja!"; + case StringId::Ready: + return "Llest!"; + case StringId::Connected: + return "Connectat"; + case StringId::Disconnected: + return "Desconnectat"; + case StringId::ImageOk: + return "Imatge OK!"; + case StringId::Error: + return "Error!"; + case StringId::YouHaveMail: + return "Tens\ncorreu!"; + case StringId::Navigation: + return "Navegacio"; + case StringId::Score: + return "Puntuacio"; + case StringId::AlarmNotSet: + return "L'alarma\nno esta\nfixada."; + case StringId::TimeToAlarm: + return "Temps fins\na l'alarma:"; + case StringId::Days: + return "Dies"; + case StringId::Hours: + return "Hores"; + case StringId::Minutes: + return "Minuts"; + case StringId::Seconds: + return "Segons"; + case StringId::Once: + return "Un cop"; + case StringId::Daily: + return "Cada dia"; + case StringId::Weekdays: + return "Dl-Dv"; + case StringId::Heads: + return "Cara"; + case StringId::Tails: + return "Creu"; + default: + return nullptr; + } + } + + const char* TranslateBasque(StringId id) { + switch (id) { + case StringId::Settings: + return "Ezarpenak"; + case StringId::Display: + return "Pantaila"; + case StringId::WakeUp: + return "Esnatu"; + case StringId::TimeFormat: + return "Ordu formatua"; + case StringId::WatchFace: + return "Erloju aurpegia"; + case StringId::Steps: + return "Urratsak"; + case StringId::HeartRate: + return "Taupadak"; + case StringId::DateTime: + return "Data eta ordua"; + case StringId::Weather: + return "Eguraldia"; + case StringId::Battery: + return "Bateria"; + case StringId::Chimes: + return "Txintak"; + case StringId::About: + return "Honi buruz"; + case StringId::Language: + return "Hizkuntza"; + case StringId::English: + return "Ingelesa"; + case StringId::Spanish: + return "Gaztelania"; + case StringId::Portuguese: + return "Portugesa"; + case StringId::Russian: + return "Errusiera"; + case StringId::French: + return "Frantsesa"; + case StringId::German: + return "Alemana"; + case StringId::Italian: + return "Italiera"; + case StringId::Turkish: + return "Turkiera"; + case StringId::Polish: + return "Poloniera"; + case StringId::Catalan: + return "Katalana"; + case StringId::Basque: + return "Euskara"; + case StringId::Back: + return "Atzera"; + case StringId::On: + return "Piztuta"; + case StringId::Off: + return "Itzalita"; + case StringId::Enabled: + return "Gaituta"; + case StringId::Disabled: + return "Desgaituta"; + case StringId::Start: + return "Hasi"; + case StringId::Stop: + return "Gelditu"; + case StringId::Pause: + return "Pausatu"; + case StringId::Reset: + return "Berrezarri"; + case StringId::Set: + return "Ezarri"; + case StringId::Charging: + return "Kargatzen"; + case StringId::FullyCharged: + return "Osoa kargatuta"; + case StringId::Discharging: + return "Deskargatzen"; + case StringId::BatteryLow: + return "Bateria baxua"; + case StringId::BatteryCritical: + return "Bateria kritikoa"; + case StringId::ReadingBattery: + return "Bateria irakurtzen"; + case StringId::Volts: + return "volt"; + case StringId::DailyStepsGoal: + return "Eguneko helburua"; + case StringId::Goal: + return "Helburua"; + case StringId::Trip: + return "Saioa"; + case StringId::Yesterday: + return "Atzo"; + case StringId::HeartRateBpm: + return "Taupadak/min"; + case StringId::NotEnoughData: + return "Datu gutxiegi,\nitxaron..."; + case StringId::NoTouchDetected: + return "Ez da ukitu"; + case StringId::Measuring: + return "Neurtzen..."; + case StringId::Stopped: + return "Geldituta"; + case StringId::BackgroundInterval: + return "Atzeko tartea"; + case StringId::Continuous: + return "Etengabe"; + case StringId::DisplayTimeout: + return "Pantaila denbora"; + case StringId::AlwaysOn: + return "Beti piztuta"; + case StringId::SetCurrentTime: + return "Ordua ezarri"; + case StringId::SetCurrentDate: + return "Data ezarri"; + case StringId::Metric: + return "Metrikoa"; + case StringId::Imperial: + return "Imperiala"; + case StringId::EveryHour: + return "Orduro"; + case StringId::Every30Minutes: + return "30 minuturo"; + case StringId::TillReboot: + return "Berrabiarazi arte"; + case StringId::FirmwareAndFiles: + return "Firmware eta fitxategiak"; + case StringId::ClearSky: + return "Zeru garbia"; + case StringId::FewClouds: + return "Hodei gutxi"; + case StringId::ScatteredClouds: + return "Hodei sakabanatuak"; + case StringId::BrokenClouds: + return "Oso hodeitsu"; + case StringId::ShowerRain: + return "Zaparradak"; + case StringId::Rain: + return "Euria"; + case StringId::Thunderstorm: + return "Ekaitza"; + case StringId::Snow: + return "Elurra"; + case StringId::Mist: + return "Lainoa"; + case StringId::Clear: + return "Garbi"; + case StringId::Cloudy: + return "Hodeitsu"; + case StringId::Showers: + return "Zaparradak"; + case StringId::Thunder: + return "Trumoia"; + case StringId::Notification: + return "Jakinarazpena"; + case StringId::IncomingCallFrom: + return "Deia"; + case StringId::Notifications: + return "Jakinarazpenak"; + case StringId::NoNotifications: + return "Ez dago jakinarazpenik"; + case StringId::TooLarge: + return "Handiegia"; + case StringId::ZeroDivision: + return "Zeroz zatitzea"; + case StringId::Warning: + return "Abisua"; + case StringId::TouchControllerError: + return "Ukimen errorea atzeman da."; + case StringId::WarrantyTip: + return "Arazorik badago eta bermea badu, jarri harremanetan saltzailearekin."; + case StringId::Proceed: + return "Jarraitu"; + case StringId::Version: + return "Bertsioa"; + case StringId::ShortRef: + return "Erref laburra"; + case StringId::FirmwareValidated: + return "Firmware hau\n#00ff00 balioztatu da#"; + case StringId::FirmwareRollbackWarning: + return "Edozein berrabiaraztek\nazken firmware\nbalioztatua itzuliko du"; + case StringId::Validate: + return "Balioztatu"; + case StringId::Rollback: + return "Leheneratu"; + case StringId::FirmwareUpdate: + return "Firmware eguner."; + case StringId::Waiting: + return "Itxaroten..."; + case StringId::WakeSensitivity: + return "Esnatze sent."; + case StringId::Calibrate: + return "Kalibratu"; + case StringId::Shake: + return "Astindu!"; + case StringId::Ready: + return "Prest!"; + case StringId::Connected: + return "Konektatuta"; + case StringId::Disconnected: + return "Deskonektatuta"; + case StringId::ImageOk: + return "Irudia ondo!"; + case StringId::Error: + return "Errorea!"; + case StringId::YouHaveMail: + return "Posta\nberria duzu!"; + case StringId::Navigation: + return "Nabigazioa"; + case StringId::Score: + return "Puntuak"; + case StringId::AlarmNotSet: + return "Alarma\nez dago\njarrita."; + case StringId::TimeToAlarm: + return "Alarmarako\ngeratzen da:"; + case StringId::Days: + return "Egun"; + case StringId::Hours: + return "Ordu"; + case StringId::Minutes: + return "Minutu"; + case StringId::Seconds: + return "Segundo"; + case StringId::Once: + return "Behin"; + case StringId::Daily: + return "Egunero"; + case StringId::Weekdays: + return "Al-Or"; + case StringId::Heads: + return "Aurpegi"; + case StringId::Tails: + return "Gurutze"; + default: + return nullptr; + } + } +} + +const char* Pinetime::Applications::Localization::Translate(Language language, StringId id) { + const auto languageIndex = static_cast(language); + const auto stringIndex = static_cast(id); + + if (languageIndex >= translations.size() || stringIndex >= StringCount) { + return ""; + } + + if (language == Language::Portuguese) { + if (const auto* translated = TranslatePortuguese(id); translated != nullptr) { + return translated; + } + return english[stringIndex]; + } + + if (language == Language::Russian) { + if (const auto* translated = TranslateRussian(id); translated != nullptr) { + return translated; + } + return english[stringIndex]; + } + + if (language == Language::French) { + if (const auto* translated = TranslateFrench(id); translated != nullptr) { + return translated; + } + return english[stringIndex]; + } + + if (language == Language::German) { + if (const auto* translated = TranslateGerman(id); translated != nullptr) { + return translated; + } + return english[stringIndex]; + } + + if (language == Language::Italian) { + if (const auto* translated = TranslateItalian(id); translated != nullptr) { + return translated; + } + return english[stringIndex]; + } + + if (language == Language::Turkish) { + if (const auto* translated = TranslateTurkish(id); translated != nullptr) { + return translated; + } + return english[stringIndex]; + } + + if (language == Language::Polish) { + if (const auto* translated = TranslatePolish(id); translated != nullptr) { + return translated; + } + return english[stringIndex]; + } + + if (language == Language::Catalan) { + if (const auto* translated = TranslateCatalan(id); translated != nullptr) { + return translated; + } + return english[stringIndex]; + } + + if (language == Language::Basque) { + if (const auto* translated = TranslateBasque(id); translated != nullptr) { + return translated; + } + return english[stringIndex]; + } + + return translations[languageIndex][stringIndex]; +} diff --git a/src/displayapp/localization/Localization.h b/src/displayapp/localization/Localization.h new file mode 100644 index 0000000000..1b756f8b3f --- /dev/null +++ b/src/displayapp/localization/Localization.h @@ -0,0 +1,149 @@ +#pragma once + +#include +#include +#include + +namespace Pinetime { + namespace Applications { + namespace Localization { + + enum class Language : uint8_t { English, Spanish, Portuguese, Russian, French, German, Italian, Turkish, Polish, Catalan, Basque, Count }; + + enum class StringId : uint8_t { + Empty, + Settings, + Display, + WakeUp, + TimeFormat, + WatchFace, + Steps, + HeartRate, + DateTime, + Weather, + Battery, + Chimes, + ShakeCalibration, + Firmware, + Ota, + Bluetooth, + About, + Language, + English, + Spanish, + Portuguese, + Russian, + French, + German, + Italian, + Turkish, + Polish, + Catalan, + Basque, + Back, + On, + Off, + Enabled, + Disabled, + Start, + Stop, + Pause, + Reset, + Set, + Charging, + FullyCharged, + Discharging, + BatteryLow, + BatteryCritical, + ReadingBattery, + Volts, + DailyStepsGoal, + Goal, + Trip, + Yesterday, + HeartRateBpm, + NotEnoughData, + NoTouchDetected, + Measuring, + Stopped, + BackgroundInterval, + Continuous, + DisplayTimeout, + AlwaysOn, + SingleTap, + DoubleTap, + RaiseWrist, + ShakeWake, + LowerWrist, + SetCurrentTime, + SetCurrentDate, + Metric, + Imperial, + EveryHour, + Every30Minutes, + TillReboot, + FirmwareAndFiles, + ClearSky, + FewClouds, + ScatteredClouds, + BrokenClouds, + ShowerRain, + Rain, + Thunderstorm, + Snow, + Mist, + Clear, + Cloudy, + Showers, + Thunder, + Notification, + IncomingCallFrom, + Notifications, + NoNotifications, + TooLarge, + ZeroDivision, + Warning, + TouchControllerError, + WarrantyTip, + Proceed, + Version, + ShortRef, + FirmwareValidated, + FirmwareRollbackWarning, + Validate, + Rollback, + FirmwareUpdate, + Waiting, + WakeSensitivity, + Calibrate, + Shake, + Ready, + Connected, + Disconnected, + ImageOk, + Error, + YouHaveMail, + Navigation, + Score, + AlarmNotSet, + TimeToAlarm, + Days, + Hours, + Minutes, + Seconds, + Once, + Daily, + Weekdays, + Heads, + Tails, + Count + }; + + static constexpr size_t LanguageCount = static_cast(Language::Count); + static constexpr size_t StringCount = static_cast(StringId::Count); + + const char* Translate(Language language, StringId id); + + } + } +} diff --git a/src/displayapp/screens/Alarm.cpp b/src/displayapp/screens/Alarm.cpp index 4cf4392157..d67b99af2a 100644 --- a/src/displayapp/screens/Alarm.cpp +++ b/src/displayapp/screens/Alarm.cpp @@ -22,10 +22,12 @@ #include "components/settings/Settings.h" #include "components/alarm/AlarmController.h" #include "components/motor/MotorController.h" +#include "displayapp/localization/Localization.h" #include "systemtask/SystemTask.h" using namespace Pinetime::Applications::Screens; using Pinetime::Controllers::AlarmController; +using namespace Pinetime::Applications::Localization; namespace { void ValueChangedHandler(void* userData) { @@ -45,14 +47,14 @@ static void StopAlarmTaskCallback(lv_task_t* task) { } Alarm::Alarm(Controllers::AlarmController& alarmController, - Controllers::Settings::ClockType clockType, + Controllers::Settings& settingsController, System::SystemTask& systemTask, Controllers::MotorController& motorController) - : alarmController {alarmController}, wakeLock(systemTask), motorController {motorController} { + : alarmController {alarmController}, settingsController {settingsController}, wakeLock(systemTask), motorController {motorController} { hourCounter.Create(); lv_obj_align(hourCounter.GetObject(), nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0); - if (clockType == Controllers::Settings::ClockType::H12) { + if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) { hourCounter.EnableTwelveHourMode(); lblampm = lv_label_create(lv_scr_act(), nullptr); @@ -252,6 +254,7 @@ void Alarm::ShowInfo() { lv_obj_set_style_local_bg_color(btnMessage, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_NAVY); if (alarmController.IsEnabled()) { + const auto language = settingsController.GetLanguage(); auto timeToAlarm = alarmController.SecondsToAlarm(); auto daysToAlarm = timeToAlarm / 86400; @@ -260,13 +263,18 @@ void Alarm::ShowInfo() { auto secToAlarm = timeToAlarm % 60; lv_label_set_text_fmt(txtMessage, - "Time to\nalarm:\n%2lu Days\n%2lu Hours\n%2lu Minutes\n%2lu Seconds", + "%s\n%2lu %s\n%2lu %s\n%2lu %s\n%2lu %s", + Translate(language, StringId::TimeToAlarm), daysToAlarm, + Translate(language, StringId::Days), hrsToAlarm, + Translate(language, StringId::Hours), minToAlarm, - secToAlarm); + Translate(language, StringId::Minutes), + secToAlarm, + Translate(language, StringId::Seconds)); } else { - lv_label_set_text_static(txtMessage, "Alarm\nis not\nset."); + lv_label_set_text_static(txtMessage, Translate(settingsController.GetLanguage(), StringId::AlarmNotSet)); } } @@ -278,15 +286,16 @@ void Alarm::HideInfo() { void Alarm::SetRecurButtonState() { using Pinetime::Controllers::AlarmController; + const auto language = settingsController.GetLanguage(); switch (alarmController.Recurrence()) { case AlarmController::RecurType::None: - lv_label_set_text_static(txtRecur, "ONCE"); + lv_label_set_text_static(txtRecur, Translate(language, StringId::Once)); break; case AlarmController::RecurType::Daily: - lv_label_set_text_static(txtRecur, "DAILY"); + lv_label_set_text_static(txtRecur, Translate(language, StringId::Daily)); break; case AlarmController::RecurType::Weekdays: - lv_label_set_text_static(txtRecur, "MON-FRI"); + lv_label_set_text_static(txtRecur, Translate(language, StringId::Weekdays)); } } diff --git a/src/displayapp/screens/Alarm.h b/src/displayapp/screens/Alarm.h index 2dde6e8754..fb00b1561b 100644 --- a/src/displayapp/screens/Alarm.h +++ b/src/displayapp/screens/Alarm.h @@ -31,7 +31,7 @@ namespace Pinetime { class Alarm : public Screen { public: explicit Alarm(Controllers::AlarmController& alarmController, - Controllers::Settings::ClockType clockType, + Controllers::Settings& settingsController, System::SystemTask& systemTask, Controllers::MotorController& motorController); ~Alarm() override; @@ -44,6 +44,7 @@ namespace Pinetime { private: Controllers::AlarmController& alarmController; + Controllers::Settings& settingsController; System::WakeLock wakeLock; Controllers::MotorController& motorController; @@ -74,7 +75,7 @@ namespace Pinetime { static Screens::Screen* Create(AppControllers& controllers) { return new Screens::Alarm(controllers.alarmController, - controllers.settingsController.GetClockType(), + controllers.settingsController, *controllers.systemTask, controllers.motorController); }; diff --git a/src/displayapp/screens/BatteryInfo.cpp b/src/displayapp/screens/BatteryInfo.cpp index 16845d53e7..b4a796c26f 100644 --- a/src/displayapp/screens/BatteryInfo.cpp +++ b/src/displayapp/screens/BatteryInfo.cpp @@ -2,10 +2,13 @@ #include "displayapp/DisplayApp.h" #include "components/battery/BatteryController.h" #include "displayapp/InfiniTimeTheme.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; -BatteryInfo::BatteryInfo(const Pinetime::Controllers::Battery& batteryController) : batteryController {batteryController} { +BatteryInfo::BatteryInfo(const Pinetime::Controllers::Battery& batteryController, Pinetime::Controllers::Settings& settingsController) + : batteryController {batteryController}, settingsController {settingsController} { batteryPercent = batteryController.PercentRemaining(); batteryVoltage = batteryController.Voltage(); @@ -24,7 +27,7 @@ BatteryInfo::BatteryInfo(const Pinetime::Controllers::Battery& batteryController lv_obj_set_style_local_line_color(chargingArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_LIME); status = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(status, "Reading Battery status"); + lv_label_set_text_static(status, Translate(settingsController.GetLanguage(), StringId::ReadingBattery)); lv_label_set_align(status, LV_LABEL_ALIGN_CENTER); lv_obj_align(status, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, -17); @@ -36,7 +39,11 @@ BatteryInfo::BatteryInfo(const Pinetime::Controllers::Battery& batteryController voltage = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(voltage, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::orange); - lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltage / 1000, batteryVoltage % 1000 / 10); + lv_label_set_text_fmt(voltage, + "%1i.%02i %s", + batteryVoltage / 1000, + batteryVoltage % 1000 / 10, + Translate(settingsController.GetLanguage(), StringId::Volts)); lv_label_set_align(voltage, LV_LABEL_ALIGN_CENTER); lv_obj_align(voltage, nullptr, LV_ALIGN_IN_BOTTOM_MID, 0, -7); @@ -56,25 +63,29 @@ void BatteryInfo::Refresh() { if (batteryController.IsCharging()) { lv_obj_set_style_local_line_color(chargingArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_LIME); - lv_label_set_text_static(status, "Charging"); + lv_label_set_text_static(status, Translate(settingsController.GetLanguage(), StringId::Charging)); } else if (batteryPercent == 100) { lv_obj_set_style_local_line_color(chargingArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_BLUE); - lv_label_set_text_static(status, "Fully charged"); + lv_label_set_text_static(status, Translate(settingsController.GetLanguage(), StringId::FullyCharged)); } else if (batteryPercent > 15) { lv_obj_set_style_local_line_color(chargingArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_GREEN); - lv_label_set_text_static(status, "Discharging"); + lv_label_set_text_static(status, Translate(settingsController.GetLanguage(), StringId::Discharging)); } else if (batteryPercent > 5) { lv_obj_set_style_local_line_color(chargingArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_ORANGE); - lv_label_set_text_static(status, "Battery low"); + lv_label_set_text_static(status, Translate(settingsController.GetLanguage(), StringId::BatteryLow)); } else { lv_obj_set_style_local_line_color(chargingArc, LV_ARC_PART_INDIC, LV_STATE_DEFAULT, Colors::deepOrange); - lv_label_set_text_static(status, "Battery critical"); + lv_label_set_text_static(status, Translate(settingsController.GetLanguage(), StringId::BatteryCritical)); } lv_label_set_text_fmt(percent, "%i%%", batteryPercent); lv_obj_align(percent, chargingArc, LV_ALIGN_CENTER, 0, 0); lv_obj_align(status, voltage, LV_ALIGN_IN_BOTTOM_MID, 0, -27); - lv_label_set_text_fmt(voltage, "%1i.%02i volts", batteryVoltage / 1000, batteryVoltage % 1000 / 10); + lv_label_set_text_fmt(voltage, + "%1i.%02i %s", + batteryVoltage / 1000, + batteryVoltage % 1000 / 10, + Translate(settingsController.GetLanguage(), StringId::Volts)); lv_arc_set_value(chargingArc, batteryPercent); } diff --git a/src/displayapp/screens/BatteryInfo.h b/src/displayapp/screens/BatteryInfo.h index 27bbaa00c0..cc82442dee 100644 --- a/src/displayapp/screens/BatteryInfo.h +++ b/src/displayapp/screens/BatteryInfo.h @@ -2,6 +2,7 @@ #include #include "displayapp/screens/Screen.h" +#include "components/settings/Settings.h" #include namespace Pinetime { @@ -14,13 +15,14 @@ namespace Pinetime { class BatteryInfo : public Screen { public: - BatteryInfo(const Pinetime::Controllers::Battery& batteryController); + BatteryInfo(const Pinetime::Controllers::Battery& batteryController, Pinetime::Controllers::Settings& settingsController); ~BatteryInfo() override; void Refresh() override; private: const Pinetime::Controllers::Battery& batteryController; + Pinetime::Controllers::Settings& settingsController; lv_obj_t* voltage; lv_obj_t* percent; diff --git a/src/displayapp/screens/Calculator.cpp b/src/displayapp/screens/Calculator.cpp index a1f093830c..567f9f55ba 100644 --- a/src/displayapp/screens/Calculator.cpp +++ b/src/displayapp/screens/Calculator.cpp @@ -2,9 +2,11 @@ #include #include "Calculator.h" #include "displayapp/InfiniTimeTheme.h" +#include "displayapp/localization/Localization.h" #include "Symbols.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; static void eventHandler(lv_obj_t* obj, lv_event_t event) { auto app = static_cast(obj->user_data); @@ -18,7 +20,7 @@ Calculator::~Calculator() { constexpr const char* const buttonMap[] = { "7", "8", "9", Symbols::backspace, "\n", "4", "5", "6", "+ -", "\n", "1", "2", "3", "* /", "\n", "0", ".", "(-)", "=", ""}; -Calculator::Calculator() { +Calculator::Calculator(Pinetime::Controllers::Settings& settingsController) : settingsController {settingsController} { resultLabel = lv_label_create(lv_scr_act(), nullptr); lv_label_set_long_mode(resultLabel, LV_LABEL_LONG_CROP); lv_label_set_align(resultLabel, LV_LABEL_ALIGN_RIGHT); @@ -275,10 +277,10 @@ void Calculator::UpdateResultLabel() const { void Calculator::UpdateValueLabel() { switch (error) { case Error::TooLarge: - lv_label_set_text_static(valueLabel, "too large"); + lv_label_set_text_static(valueLabel, Translate(settingsController.GetLanguage(), StringId::TooLarge)); break; case Error::ZeroDivision: - lv_label_set_text_static(valueLabel, "zero division"); + lv_label_set_text_static(valueLabel, Translate(settingsController.GetLanguage(), StringId::ZeroDivision)); break; case Error::None: default: { diff --git a/src/displayapp/screens/Calculator.h b/src/displayapp/screens/Calculator.h index 25bb67a7e9..33a6ecdef6 100644 --- a/src/displayapp/screens/Calculator.h +++ b/src/displayapp/screens/Calculator.h @@ -3,6 +3,7 @@ #include "displayapp/screens/Screen.h" #include "displayapp/apps/Apps.h" #include "displayapp/Controllers.h" +#include "components/settings/Settings.h" #include "Symbols.h" namespace { @@ -23,7 +24,7 @@ namespace Pinetime { public: ~Calculator() override; - Calculator(); + explicit Calculator(Pinetime::Controllers::Settings& settingsController); void OnButtonEvent(lv_obj_t* obj, lv_event_t event); @@ -31,6 +32,7 @@ namespace Pinetime { lv_obj_t* buttonMatrix {}; lv_obj_t* valueLabel {}; lv_obj_t* resultLabel {}; + Pinetime::Controllers::Settings& settingsController; void Eval(); void ResetInput(); @@ -75,8 +77,8 @@ namespace Pinetime { static constexpr Apps app = Apps::Calculator; static constexpr const char* icon = Screens::Symbols::calculator; - static Screens::Screen* Create(AppControllers& /* controllers */) { - return new Screens::Calculator(); + static Screens::Screen* Create(AppControllers& controllers) { + return new Screens::Calculator(controllers.settingsController); }; static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) { diff --git a/src/displayapp/screens/CheckboxList.cpp b/src/displayapp/screens/CheckboxList.cpp index 9eb80bbad4..7213b749f2 100644 --- a/src/displayapp/screens/CheckboxList.cpp +++ b/src/displayapp/screens/CheckboxList.cpp @@ -22,6 +22,8 @@ CheckboxList::CheckboxList(const uint8_t screenID, OnValueChanged {std::move(OnValueChanged)}, options {options}, value {originalValue}, + title {nullptr}, + icon {nullptr}, pageIndicator(screenID, numScreens) { // Set the background to Black lv_obj_set_style_local_bg_color(lv_scr_act(), LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); @@ -42,12 +44,12 @@ CheckboxList::CheckboxList(const uint8_t screenID, lv_obj_set_height(container1, LV_VER_RES - 50); lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT); - lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr); + title = lv_label_create(lv_scr_act(), nullptr); lv_label_set_text_static(title, optionsTitle); lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 10, 15); - lv_obj_t* icon = lv_label_create(lv_scr_act(), nullptr); + icon = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(icon, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE); lv_label_set_text_static(icon, optionsSymbol); lv_label_set_align(icon, LV_LABEL_ALIGN_CENTER); @@ -73,11 +75,35 @@ CheckboxList::CheckboxList(const uint8_t screenID, CheckboxList::~CheckboxList() { lv_obj_clean(lv_scr_act()); - OnValueChanged(value); +} + +void CheckboxList::SetTitle(const char* optionsTitle) { + lv_label_set_text_static(title, optionsTitle); + lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 10, 15); + lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0); +} + +void CheckboxList::SetSymbol(const char* optionsSymbol) { + lv_label_set_text_static(icon, optionsSymbol); + lv_obj_align(icon, title, LV_ALIGN_OUT_LEFT_MID, -10, 0); +} + +void CheckboxList::SetOptions(std::array updatedOptions) { + options = updatedOptions; + + for (size_t i = 0; i < options.size(); i++) { + if (strcmp(options[i].name, "")) { + lv_checkbox_set_text(cbOption[i], options[i].name); + if (!options[i].enabled) { + lv_checkbox_set_disabled(cbOption[i]); + } + } + } } void CheckboxList::UpdateSelected(lv_obj_t* object, lv_event_t event) { if (event == LV_EVENT_VALUE_CHANGED) { + const auto previousValue = value; for (unsigned int i = 0; i < options.size(); i++) { if (strcmp(options[i].name, "")) { if (object == cbOption[i]) { @@ -91,5 +117,9 @@ void CheckboxList::UpdateSelected(lv_obj_t* object, lv_event_t event) { } } } + + if (value != previousValue) { + OnValueChanged(value); + } } } diff --git a/src/displayapp/screens/CheckboxList.h b/src/displayapp/screens/CheckboxList.h index c6119970a1..c2460c15ae 100644 --- a/src/displayapp/screens/CheckboxList.h +++ b/src/displayapp/screens/CheckboxList.h @@ -30,6 +30,9 @@ namespace Pinetime { std::array options); ~CheckboxList() override; void UpdateSelected(lv_obj_t* object, lv_event_t event); + void SetTitle(const char* optionsTitle); + void SetSymbol(const char* optionsSymbol); + void SetOptions(std::array updatedOptions); private: const uint8_t screenID; @@ -37,6 +40,8 @@ namespace Pinetime { std::array options; std::array cbOption; uint32_t value; + lv_obj_t* title; + lv_obj_t* icon; Widgets::PageIndicator pageIndicator; }; diff --git a/src/displayapp/screens/Dice.cpp b/src/displayapp/screens/Dice.cpp index 302c5f3fb2..ff5ecee04b 100644 --- a/src/displayapp/screens/Dice.cpp +++ b/src/displayapp/screens/Dice.cpp @@ -4,8 +4,10 @@ #include "components/settings/Settings.h" #include "components/motor/MotorController.h" #include "components/motion/MotionController.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { lv_obj_t* MakeLabel(lv_font_t* font, @@ -166,10 +168,10 @@ void Dice::Roll() { if (dCounter.GetValue() == 2) { switch (resultTotal) { case 1: - lv_label_set_text(resultIndividualLabel, "HEADS"); + lv_label_set_text(resultIndividualLabel, Translate(settingsController.GetLanguage(), StringId::Heads)); break; case 2: - lv_label_set_text(resultIndividualLabel, "TAILS"); + lv_label_set_text(resultIndividualLabel, Translate(settingsController.GetLanguage(), StringId::Tails)); break; } } diff --git a/src/displayapp/screens/Error.cpp b/src/displayapp/screens/Error.cpp index 6f826b873b..a8940d927a 100644 --- a/src/displayapp/screens/Error.cpp +++ b/src/displayapp/screens/Error.cpp @@ -1,6 +1,8 @@ #include "displayapp/screens/Error.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { void ButtonEventCallback(lv_obj_t* obj, lv_event_t /*event*/) { @@ -9,11 +11,11 @@ namespace { } } -Error::Error(System::BootErrors error) { +Error::Error(System::BootErrors error, Pinetime::Controllers::Settings& settingsController) : settingsController {settingsController} { lv_obj_t* warningLabel = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(warningLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE); - lv_label_set_text_static(warningLabel, "Warning"); + lv_label_set_text_static(warningLabel, Translate(settingsController.GetLanguage(), StringId::Warning)); lv_obj_align(warningLabel, nullptr, LV_ALIGN_IN_TOP_MID, 0, 0); lv_obj_t* causeLabel = lv_label_create(lv_scr_act(), nullptr); @@ -22,13 +24,13 @@ Error::Error(System::BootErrors error) { lv_obj_align(causeLabel, warningLabel, LV_ALIGN_OUT_BOTTOM_MID, 0, 0); if (error == System::BootErrors::TouchController) { - lv_label_set_text_static(causeLabel, "Touch controller error detected."); + lv_label_set_text_static(causeLabel, Translate(settingsController.GetLanguage(), StringId::TouchControllerError)); } lv_obj_t* tipLabel = lv_label_create(lv_scr_act(), nullptr); lv_label_set_long_mode(tipLabel, LV_LABEL_LONG_BREAK); lv_obj_set_width(tipLabel, LV_HOR_RES); - lv_label_set_text_static(tipLabel, "If you encounter problems and your device is under warranty, contact the devices seller."); + lv_label_set_text_static(tipLabel, Translate(settingsController.GetLanguage(), StringId::WarrantyTip)); lv_obj_align(tipLabel, causeLabel, LV_ALIGN_OUT_BOTTOM_MID, 0, 0); btnOk = lv_btn_create(lv_scr_act(), nullptr); @@ -37,7 +39,7 @@ Error::Error(System::BootErrors error) { lv_obj_set_size(btnOk, LV_HOR_RES, 50); lv_obj_align(btnOk, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0); lv_obj_t* lblOk = lv_label_create(btnOk, nullptr); - lv_label_set_text_static(lblOk, "Proceed"); + lv_label_set_text_static(lblOk, Translate(settingsController.GetLanguage(), StringId::Proceed)); lv_obj_set_style_local_bg_color(btnOk, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE); } diff --git a/src/displayapp/screens/Error.h b/src/displayapp/screens/Error.h index 8e7d47088d..8a7fdf85b0 100644 --- a/src/displayapp/screens/Error.h +++ b/src/displayapp/screens/Error.h @@ -1,6 +1,7 @@ #pragma once #include "displayapp/screens/Screen.h" +#include "components/settings/Settings.h" #include "BootErrors.h" #include @@ -9,13 +10,14 @@ namespace Pinetime { namespace Screens { class Error : public Screen { public: - Error(System::BootErrors error); + Error(System::BootErrors error, Pinetime::Controllers::Settings& settingsController); ~Error() override; void ButtonEventHandler(); private: lv_obj_t* btnOk; + Pinetime::Controllers::Settings& settingsController; }; } } diff --git a/src/displayapp/screens/FirmwareUpdate.cpp b/src/displayapp/screens/FirmwareUpdate.cpp index 7d00ef3921..9bbc2ddd80 100644 --- a/src/displayapp/screens/FirmwareUpdate.cpp +++ b/src/displayapp/screens/FirmwareUpdate.cpp @@ -3,13 +3,16 @@ #include "components/ble/BleController.h" #include "displayapp/DisplayApp.h" #include "displayapp/InfiniTimeTheme.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; -FirmwareUpdate::FirmwareUpdate(const Pinetime::Controllers::Ble& bleController) : bleController {bleController} { +FirmwareUpdate::FirmwareUpdate(const Pinetime::Controllers::Ble& bleController, Pinetime::Controllers::Settings& settingsController) + : bleController {bleController}, settingsController {settingsController} { titleLabel = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(titleLabel, "Firmware update"); + lv_label_set_text_static(titleLabel, Translate(settingsController.GetLanguage(), StringId::FirmwareUpdate)); lv_obj_align(titleLabel, nullptr, LV_ALIGN_IN_TOP_MID, 0, 50); bar1 = lv_bar_create(lv_scr_act(), nullptr); @@ -22,7 +25,7 @@ FirmwareUpdate::FirmwareUpdate(const Pinetime::Controllers::Ble& bleController) lv_bar_set_value(bar1, 0, LV_ANIM_OFF); percentLabel = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(percentLabel, "Waiting..."); + lv_label_set_text_static(percentLabel, Translate(settingsController.GetLanguage(), StringId::Waiting)); lv_label_set_recolor(percentLabel, true); lv_obj_set_auto_realign(percentLabel, true); lv_obj_align(percentLabel, bar1, LV_ALIGN_OUT_TOP_MID, 0, 60); @@ -85,11 +88,11 @@ void FirmwareUpdate::DisplayProgression() const { } void FirmwareUpdate::UpdateValidated() { - lv_label_set_text_static(percentLabel, "#00ff00 Image Ok!#"); + lv_label_set_text_fmt(percentLabel, "#00ff00 %s#", Translate(settingsController.GetLanguage(), StringId::ImageOk)); } void FirmwareUpdate::UpdateError() { - lv_label_set_text_static(percentLabel, "#ff0000 Error!#"); + lv_label_set_text_fmt(percentLabel, "#ff0000 %s#", Translate(settingsController.GetLanguage(), StringId::Error)); startTime = xTaskGetTickCount(); } diff --git a/src/displayapp/screens/FirmwareUpdate.h b/src/displayapp/screens/FirmwareUpdate.h index ebe7c2d74d..bfd2b049a3 100644 --- a/src/displayapp/screens/FirmwareUpdate.h +++ b/src/displayapp/screens/FirmwareUpdate.h @@ -1,6 +1,7 @@ #pragma once #include "displayapp/screens/Screen.h" +#include "components/settings/Settings.h" #include #include "FreeRTOS.h" @@ -14,7 +15,7 @@ namespace Pinetime { class FirmwareUpdate : public Screen { public: - FirmwareUpdate(const Pinetime::Controllers::Ble& bleController); + FirmwareUpdate(const Pinetime::Controllers::Ble& bleController, Pinetime::Controllers::Settings& settingsController); ~FirmwareUpdate() override; void Refresh() override; @@ -22,6 +23,7 @@ namespace Pinetime { private: enum class States { Idle, Running, Validated, Error }; const Pinetime::Controllers::Ble& bleController; + Pinetime::Controllers::Settings& settingsController; lv_obj_t* bar1; lv_obj_t* percentLabel; lv_obj_t* titleLabel; diff --git a/src/displayapp/screens/FirmwareValidation.cpp b/src/displayapp/screens/FirmwareValidation.cpp index 8fc9d251cb..6da4b24bb8 100644 --- a/src/displayapp/screens/FirmwareValidation.cpp +++ b/src/displayapp/screens/FirmwareValidation.cpp @@ -5,8 +5,10 @@ #include "displayapp/DisplayApp.h" #include "displayapp/InfiniTimeTheme.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { void ButtonEventHandler(lv_obj_t* obj, lv_event_t event) { @@ -15,9 +17,11 @@ namespace { } } -FirmwareValidation::FirmwareValidation(Pinetime::Controllers::FirmwareValidator& validator) : validator {validator} { +FirmwareValidation::FirmwareValidation(Pinetime::Controllers::FirmwareValidator& validator, + Pinetime::Controllers::Settings& settingsController) + : validator {validator}, settingsController {settingsController} { lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(title, "Firmware"); + lv_label_set_text_static(title, Translate(settingsController.GetLanguage(), StringId::Firmware)); lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 10, 15); @@ -30,11 +34,13 @@ FirmwareValidation::FirmwareValidation(Pinetime::Controllers::FirmwareValidator& labelVersion = lv_label_create(lv_scr_act(), nullptr); lv_label_set_recolor(labelVersion, true); lv_label_set_text_fmt(labelVersion, - "#808080 Version# %lu.%lu.%lu\n" - "#808080 Short Ref# %s\n", + "#808080 %s# %lu.%lu.%lu\n" + "#808080 %s# %s\n", + Translate(settingsController.GetLanguage(), StringId::Version), Version::Major(), Version::Minor(), Version::Patch(), + Translate(settingsController.GetLanguage(), StringId::ShortRef), Version::GitCommitHash()); lv_obj_align(labelVersion, nullptr, LV_ALIGN_CENTER, 0, -40); lv_label_set_align(labelVersion, LV_LABEL_ALIGN_CENTER); @@ -46,10 +52,10 @@ FirmwareValidation::FirmwareValidation(Pinetime::Controllers::FirmwareValidator& lv_label_set_recolor(labelIsValidated, true); if (validator.IsValidated()) { - lv_label_set_text_static(labelIsValidated, "This firmware has\nbeen #00ff00 validated#"); + lv_label_set_text_static(labelIsValidated, Translate(settingsController.GetLanguage(), StringId::FirmwareValidated)); lv_obj_align(labelIsValidated, nullptr, LV_ALIGN_CENTER, 0, 10); } else { - lv_label_set_text_static(labelIsValidated, "Any reboot will\nrollback to last\nvalidated firmware"); + lv_label_set_text_static(labelIsValidated, Translate(settingsController.GetLanguage(), StringId::FirmwareRollbackWarning)); buttonValidate = lv_btn_create(lv_scr_act(), nullptr); buttonValidate->user_data = this; @@ -59,7 +65,7 @@ FirmwareValidation::FirmwareValidation(Pinetime::Controllers::FirmwareValidator& lv_obj_set_style_local_bg_color(buttonValidate, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::highlight); labelButtonValidate = lv_label_create(buttonValidate, nullptr); - lv_label_set_text_static(labelButtonValidate, "Validate"); + lv_label_set_text_static(labelButtonValidate, Translate(settingsController.GetLanguage(), StringId::Validate)); buttonReset = lv_btn_create(lv_scr_act(), nullptr); buttonReset->user_data = this; @@ -69,7 +75,7 @@ FirmwareValidation::FirmwareValidation(Pinetime::Controllers::FirmwareValidator& lv_obj_set_event_cb(buttonReset, ButtonEventHandler); labelButtonReset = lv_label_create(buttonReset, nullptr); - lv_label_set_text_static(labelButtonReset, "Rollback"); + lv_label_set_text_static(labelButtonReset, Translate(settingsController.GetLanguage(), StringId::Rollback)); } } diff --git a/src/displayapp/screens/FirmwareValidation.h b/src/displayapp/screens/FirmwareValidation.h index a30f8c59db..72d75342cd 100644 --- a/src/displayapp/screens/FirmwareValidation.h +++ b/src/displayapp/screens/FirmwareValidation.h @@ -1,6 +1,7 @@ #pragma once #include "displayapp/screens/Screen.h" +#include "components/settings/Settings.h" #include namespace Pinetime { @@ -13,13 +14,14 @@ namespace Pinetime { class FirmwareValidation : public Screen { public: - FirmwareValidation(Pinetime::Controllers::FirmwareValidator& validator); + FirmwareValidation(Pinetime::Controllers::FirmwareValidator& validator, Pinetime::Controllers::Settings& settingsController); ~FirmwareValidation() override; void OnButtonEvent(lv_obj_t* object, lv_event_t event); private: Pinetime::Controllers::FirmwareValidator& validator; + Pinetime::Controllers::Settings& settingsController; lv_obj_t* labelVersion; lv_obj_t* labelIsValidated; diff --git a/src/displayapp/screens/HeartRate.cpp b/src/displayapp/screens/HeartRate.cpp index 14c873e201..44c288e78b 100644 --- a/src/displayapp/screens/HeartRate.cpp +++ b/src/displayapp/screens/HeartRate.cpp @@ -4,20 +4,22 @@ #include "displayapp/DisplayApp.h" #include "displayapp/InfiniTimeTheme.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { - const char* ToString(Pinetime::Controllers::HeartRateController::States s) { + const char* ToString(Pinetime::Controllers::Settings& settingsController, Pinetime::Controllers::HeartRateController::States s) { switch (s) { case Pinetime::Controllers::HeartRateController::States::NotEnoughData: - return "Not enough data,\nplease wait..."; + return Translate(settingsController.GetLanguage(), StringId::NotEnoughData); case Pinetime::Controllers::HeartRateController::States::NoTouch: - return "No touch detected"; + return Translate(settingsController.GetLanguage(), StringId::NoTouchDetected); case Pinetime::Controllers::HeartRateController::States::Running: - return "Measuring..."; + return Translate(settingsController.GetLanguage(), StringId::Measuring); case Pinetime::Controllers::HeartRateController::States::Stopped: - return "Stopped"; + return Translate(settingsController.GetLanguage(), StringId::Stopped); } return ""; } @@ -28,8 +30,10 @@ namespace { } } -HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, System::SystemTask& systemTask) - : heartRateController {heartRateController}, wakeLock(systemTask) { +HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, + Controllers::Settings& settingsController, + System::SystemTask& systemTask) + : heartRateController {heartRateController}, settingsController {settingsController}, wakeLock(systemTask) { bool isHrRunning = heartRateController.State() != Controllers::HeartRateController::States::Stopped; label_hr = lv_label_create(lv_scr_act(), nullptr); @@ -45,12 +49,12 @@ HeartRate::HeartRate(Controllers::HeartRateController& heartRateController, Syst lv_obj_align(label_hr, nullptr, LV_ALIGN_CENTER, 0, -40); label_bpm = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(label_bpm, "Heart rate BPM"); + lv_label_set_text_static(label_bpm, Translate(settingsController.GetLanguage(), StringId::HeartRateBpm)); lv_obj_align(label_bpm, label_hr, LV_ALIGN_OUT_TOP_MID, 0, -20); label_status = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(label_status, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY); - lv_label_set_text_static(label_status, ToString(Pinetime::Controllers::HeartRateController::States::NotEnoughData)); + lv_label_set_text_static(label_status, ToString(settingsController, Pinetime::Controllers::HeartRateController::States::NotEnoughData)); lv_obj_align(label_status, label_hr, LV_ALIGN_OUT_BOTTOM_MID, 0, 10); @@ -91,7 +95,7 @@ void HeartRate::Refresh() { } } - lv_label_set_text_static(label_status, ToString(state)); + lv_label_set_text_static(label_status, ToString(settingsController, state)); lv_obj_align(label_status, label_hr, LV_ALIGN_OUT_BOTTOM_MID, 0, 10); } @@ -113,8 +117,8 @@ void HeartRate::OnStartStopEvent(lv_event_t event) { void HeartRate::UpdateStartStopButton(bool isRunning) { if (isRunning) { - lv_label_set_text_static(label_startStop, "Stop"); + lv_label_set_text_static(label_startStop, Translate(settingsController.GetLanguage(), StringId::Stop)); } else { - lv_label_set_text_static(label_startStop, "Start"); + lv_label_set_text_static(label_startStop, Translate(settingsController.GetLanguage(), StringId::Start)); } } diff --git a/src/displayapp/screens/HeartRate.h b/src/displayapp/screens/HeartRate.h index 69c935de08..9b7cb15e0c 100644 --- a/src/displayapp/screens/HeartRate.h +++ b/src/displayapp/screens/HeartRate.h @@ -3,6 +3,7 @@ #include #include #include "displayapp/screens/Screen.h" +#include "components/settings/Settings.h" #include "systemtask/SystemTask.h" #include "systemtask/WakeLock.h" #include "Symbols.h" @@ -19,7 +20,9 @@ namespace Pinetime { class HeartRate : public Screen { public: - HeartRate(Controllers::HeartRateController& HeartRateController, System::SystemTask& systemTask); + HeartRate(Controllers::HeartRateController& HeartRateController, + Controllers::Settings& settingsController, + System::SystemTask& systemTask); ~HeartRate() override; void Refresh() override; @@ -28,6 +31,7 @@ namespace Pinetime { private: Controllers::HeartRateController& heartRateController; + Controllers::Settings& settingsController; Pinetime::System::WakeLock wakeLock; void UpdateStartStopButton(bool isRunning); lv_obj_t* label_hr; @@ -46,7 +50,7 @@ namespace Pinetime { static constexpr const char* icon = Screens::Symbols::heartBeat; static Screens::Screen* Create(AppControllers& controllers) { - return new Screens::HeartRate(controllers.heartRateController, *controllers.systemTask); + return new Screens::HeartRate(controllers.heartRateController, controllers.settingsController, *controllers.systemTask); }; static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) { diff --git a/src/displayapp/screens/List.cpp b/src/displayapp/screens/List.cpp index 264b4fc98b..0a533c20d3 100644 --- a/src/displayapp/screens/List.cpp +++ b/src/displayapp/screens/List.cpp @@ -1,9 +1,11 @@ #include "displayapp/screens/List.h" #include "displayapp/DisplayApp.h" +#include "displayapp/localization/Localization.h" #include "displayapp/screens/Symbols.h" #include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { void ButtonEventHandler(lv_obj_t* obj, lv_event_t event) { @@ -62,7 +64,7 @@ List::List(uint8_t screenID, lv_obj_align(icon, nullptr, LV_ALIGN_IN_LEFT_MID, 0, 0); lv_obj_t* text = lv_label_create(itemApps[i], nullptr); - lv_label_set_text_fmt(text, "%s", applications[i].name); + lv_label_set_text_fmt(text, "%s", Translate(settingsController.GetLanguage(), applications[i].name)); lv_obj_align(text, icon, LV_ALIGN_OUT_RIGHT_MID, 0, 0); } } diff --git a/src/displayapp/screens/List.h b/src/displayapp/screens/List.h index 17a25f8209..0337c1f895 100644 --- a/src/displayapp/screens/List.h +++ b/src/displayapp/screens/List.h @@ -6,6 +6,7 @@ #include "displayapp/screens/Screen.h" #include "displayapp/widgets/PageIndicator.h" #include "displayapp/apps/Apps.h" +#include "displayapp/localization/Localization.h" #include "components/settings/Settings.h" #define MAXLISTITEMS 4 @@ -17,7 +18,7 @@ namespace Pinetime { public: struct Applications { const char* icon; - const char* name; + Pinetime::Applications::Localization::StringId name; Pinetime::Applications::Apps application; }; diff --git a/src/displayapp/screens/Navigation.cpp b/src/displayapp/screens/Navigation.cpp index fc2733e81d..a2f7ead4fa 100644 --- a/src/displayapp/screens/Navigation.cpp +++ b/src/displayapp/screens/Navigation.cpp @@ -19,9 +19,11 @@ #include #include "displayapp/DisplayApp.h" #include "components/ble/NavigationService.h" +#include "displayapp/localization/Localization.h" #include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; /* Notes about the navigation icons : * - Icons are generated from a TTF font converted in PNG images. Those images are all appended @@ -190,7 +192,8 @@ namespace { * Navigation watchapp * */ -Navigation::Navigation(Pinetime::Controllers::NavigationService& nav) : navService(nav) { +Navigation::Navigation(Pinetime::Controllers::NavigationService& nav, Pinetime::Controllers::Settings& settingsController) + : navService(nav), settingsController(settingsController) { const auto& image = GetIcon("flag"); imgFlag = lv_img_create(lv_scr_act(), nullptr); lv_img_set_auto_size(imgFlag, false); @@ -206,7 +209,7 @@ Navigation::Navigation(Pinetime::Controllers::NavigationService& nav) : navServi lv_label_set_long_mode(txtNarrative, LV_LABEL_LONG_DOT); lv_obj_set_width(txtNarrative, LV_HOR_RES); lv_obj_set_height(txtNarrative, 80); - lv_label_set_text_static(txtNarrative, "Navigation"); + lv_label_set_text_static(txtNarrative, Translate(settingsController.GetLanguage(), StringId::Navigation)); lv_label_set_align(txtNarrative, LV_LABEL_ALIGN_CENTER); lv_obj_align(txtNarrative, nullptr, LV_ALIGN_CENTER, 0, 30); diff --git a/src/displayapp/screens/Navigation.h b/src/displayapp/screens/Navigation.h index 95b23d7160..c029659663 100644 --- a/src/displayapp/screens/Navigation.h +++ b/src/displayapp/screens/Navigation.h @@ -36,7 +36,7 @@ namespace Pinetime { namespace Screens { class Navigation : public Screen { public: - explicit Navigation(Pinetime::Controllers::NavigationService& nav); + explicit Navigation(Pinetime::Controllers::NavigationService& nav, Pinetime::Controllers::Settings& settingsController); ~Navigation() override; void Refresh() override; @@ -49,6 +49,7 @@ namespace Pinetime { lv_obj_t* barProgress; Pinetime::Controllers::NavigationService& navService; + Pinetime::Controllers::Settings& settingsController; std::string flag; std::string narrative; @@ -65,7 +66,7 @@ namespace Pinetime { static constexpr const char* icon = Screens::Symbols::map; static Screens::Screen* Create(AppControllers& controllers) { - return new Screens::Navigation(*controllers.navigationService); + return new Screens::Navigation(*controllers.navigationService, controllers.settingsController); }; static bool IsAvailable(Pinetime::Controllers::FS& filesystem) { diff --git a/src/displayapp/screens/Notifications.cpp b/src/displayapp/screens/Notifications.cpp index 837c4683aa..0541cf2db3 100644 --- a/src/displayapp/screens/Notifications.cpp +++ b/src/displayapp/screens/Notifications.cpp @@ -5,8 +5,10 @@ #include "displayapp/screens/Symbols.h" #include #include "displayapp/InfiniTimeTheme.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; extern lv_font_t jetbrains_mono_extrabold_compressed; extern lv_font_t jetbrains_mono_bold_20; @@ -14,12 +16,14 @@ Notifications::Notifications(DisplayApp* app, Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::AlertNotificationService& alertNotificationService, Pinetime::Controllers::MotorController& motorController, + Pinetime::Controllers::Settings& settingsController, System::SystemTask& systemTask, Modes mode) : app {app}, notificationManager {notificationManager}, alertNotificationService {alertNotificationService}, motorController {motorController}, + settingsController {settingsController}, wakeLock(systemTask), mode {mode} { @@ -33,10 +37,11 @@ Notifications::Notifications(DisplayApp* app, notification.category, notificationManager.NbNotifications(), alertNotificationService, - motorController); + motorController, + settingsController.GetLanguage()); validDisplay = true; } else { - currentItem = std::make_unique(alertNotificationService, motorController); + currentItem = std::make_unique(alertNotificationService, motorController, settingsController.GetLanguage()); validDisplay = false; } if (mode == Modes::Preview) { @@ -109,7 +114,8 @@ void Notifications::Refresh() { notification.category, notificationManager.NbNotifications(), alertNotificationService, - motorController); + motorController, + settingsController.GetLanguage()); } else { running = false; } @@ -202,7 +208,8 @@ bool Notifications::OnTouchEvent(Pinetime::Applications::TouchEvents event) { previousNotification.category, notificationManager.NbNotifications(), alertNotificationService, - motorController); + motorController, + settingsController.GetLanguage()); } return true; case Pinetime::Applications::TouchEvents::SwipeUp: { @@ -229,7 +236,8 @@ bool Notifications::OnTouchEvent(Pinetime::Applications::TouchEvents event) { nextNotification.category, notificationManager.NbNotifications(), alertNotificationService, - motorController); + motorController, + settingsController.GetLanguage()); } return true; default: @@ -245,14 +253,16 @@ namespace { } Notifications::NotificationItem::NotificationItem(Pinetime::Controllers::AlertNotificationService& alertNotificationService, - Pinetime::Controllers::MotorController& motorController) - : NotificationItem("Notifications", - "No notifications to display", + Pinetime::Controllers::MotorController& motorController, + Language language) + : NotificationItem(Translate(language, StringId::Notifications), + Translate(language, StringId::NoNotifications), 0, Controllers::NotificationManager::Categories::Unknown, 0, alertNotificationService, - motorController) { + motorController, + language) { } Notifications::NotificationItem::NotificationItem(const char* title, @@ -261,8 +271,9 @@ Notifications::NotificationItem::NotificationItem(const char* title, Controllers::NotificationManager::Categories category, uint8_t notifNb, Pinetime::Controllers::AlertNotificationService& alertNotificationService, - Pinetime::Controllers::MotorController& motorController) - : alertNotificationService {alertNotificationService}, motorController {motorController} { + Pinetime::Controllers::MotorController& motorController, + Language language) + : alertNotificationService {alertNotificationService}, motorController {motorController}, language {language} { container = lv_cont_create(lv_scr_act(), nullptr); lv_obj_set_size(container, LV_HOR_RES, LV_VER_RES); lv_obj_set_style_local_bg_color(container, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_BLACK); @@ -288,7 +299,7 @@ Notifications::NotificationItem::NotificationItem(const char* title, lv_obj_t* alert_type = lv_label_create(container, nullptr); lv_obj_set_style_local_text_color(alert_type, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::orange); if (title == nullptr) { - lv_label_set_text_static(alert_type, "Notification"); + lv_label_set_text_static(alert_type, Translate(language, StringId::Notification)); } else { // copy title to label and replace newlines with spaces lv_label_set_text(alert_type, title); @@ -313,7 +324,7 @@ Notifications::NotificationItem::NotificationItem(const char* title, break; case Controllers::NotificationManager::Categories::IncomingCall: { lv_obj_set_height(subject_container, 108); - lv_label_set_text_static(alert_subject, "Incoming call from"); + lv_label_set_text_static(alert_subject, Translate(language, StringId::IncomingCallFrom)); lv_obj_t* alert_caller = lv_label_create(subject_container, nullptr); lv_obj_align(alert_caller, alert_subject, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0); diff --git a/src/displayapp/screens/Notifications.h b/src/displayapp/screens/Notifications.h index 8488dc5bb2..0a7e8a3717 100644 --- a/src/displayapp/screens/Notifications.h +++ b/src/displayapp/screens/Notifications.h @@ -5,6 +5,8 @@ #include #include #include "displayapp/screens/Screen.h" +#include "components/settings/Settings.h" +#include "displayapp/localization/Localization.h" #include "components/ble/NotificationManager.h" #include "components/motor/MotorController.h" #include "systemtask/SystemTask.h" @@ -25,6 +27,7 @@ namespace Pinetime { Pinetime::Controllers::NotificationManager& notificationManager, Pinetime::Controllers::AlertNotificationService& alertNotificationService, Pinetime::Controllers::MotorController& motorController, + Pinetime::Controllers::Settings& settingsController, System::SystemTask& systemTask, Modes mode); ~Notifications() override; @@ -38,14 +41,16 @@ namespace Pinetime { class NotificationItem { public: NotificationItem(Pinetime::Controllers::AlertNotificationService& alertNotificationService, - Pinetime::Controllers::MotorController& motorController); + Pinetime::Controllers::MotorController& motorController, + Pinetime::Applications::Localization::Language language); NotificationItem(const char* title, const char* msg, uint8_t notifNr, Controllers::NotificationManager::Categories, uint8_t notifNb, Pinetime::Controllers::AlertNotificationService& alertNotificationService, - Pinetime::Controllers::MotorController& motorController); + Pinetime::Controllers::MotorController& motorController, + Pinetime::Applications::Localization::Language language); ~NotificationItem(); bool IsRunning() const { @@ -65,6 +70,7 @@ namespace Pinetime { lv_obj_t* label_reject; Pinetime::Controllers::AlertNotificationService& alertNotificationService; Pinetime::Controllers::MotorController& motorController; + Pinetime::Applications::Localization::Language language; bool running = true; }; @@ -74,6 +80,7 @@ namespace Pinetime { Pinetime::Controllers::NotificationManager& notificationManager; Pinetime::Controllers::AlertNotificationService& alertNotificationService; Pinetime::Controllers::MotorController& motorController; + Pinetime::Controllers::Settings& settingsController; System::WakeLock wakeLock; Modes mode = Modes::Normal; std::unique_ptr currentItem; diff --git a/src/displayapp/screens/Steps.cpp b/src/displayapp/screens/Steps.cpp index 2e73dab512..4b3a8f9bc7 100644 --- a/src/displayapp/screens/Steps.cpp +++ b/src/displayapp/screens/Steps.cpp @@ -2,15 +2,13 @@ #include #include "displayapp/DisplayApp.h" #include "displayapp/InfiniTimeTheme.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; using Days = Pinetime::Controllers::MotionController::Days; -namespace { - constexpr const char* yesterdayStr = "Yest: %5lu"; -} - static void lap_event_handler(lv_obj_t* obj, lv_event_t event) { auto* steps = static_cast(obj->user_data); steps->lapBtnEventHandler(event); @@ -44,18 +42,22 @@ Steps::Steps(Controllers::MotionController& motionController, Controllers::Setti lv_obj_t* lstepsL = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(lstepsL, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); - lv_label_set_text_static(lstepsL, "Steps"); + lv_label_set_text_static(lstepsL, Translate(settingsController.GetLanguage(), StringId::Steps)); lv_obj_align(lstepsL, lSteps, LV_ALIGN_OUT_BOTTOM_MID, 0, 0); lStepsYesterday = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(lStepsYesterday, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::lightGray); - lv_label_set_text_fmt(lStepsYesterday, yesterdayStr, motionController.NbSteps(Days::Yesterday)); + lv_label_set_text_fmt(lStepsYesterday, + "%s: %5lu", + Translate(settingsController.GetLanguage(), StringId::Yesterday), + motionController.NbSteps(Days::Yesterday)); lv_label_set_align(lStepsYesterday, LV_LABEL_ALIGN_CENTER); lv_obj_align(lStepsYesterday, lSteps, LV_ALIGN_OUT_BOTTOM_MID, 0, 20); lv_obj_t* lstepsGoal = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(lstepsGoal, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_CYAN); - lv_label_set_text_fmt(lstepsGoal, "Goal: %5lu", settingsController.GetStepsGoal()); + lv_label_set_text_fmt( + lstepsGoal, "%s: %5lu", Translate(settingsController.GetLanguage(), StringId::Goal), settingsController.GetStepsGoal()); lv_label_set_align(lstepsGoal, LV_LABEL_ALIGN_CENTER); lv_obj_align(lstepsGoal, lSteps, LV_ALIGN_OUT_BOTTOM_MID, 0, 40); @@ -67,13 +69,14 @@ Steps::Steps(Controllers::MotionController& motionController, Controllers::Setti lv_obj_set_style_local_bg_color(resetBtn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt); lv_obj_align(resetBtn, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0); resetButtonLabel = lv_label_create(resetBtn, nullptr); - lv_label_set_text_static(resetButtonLabel, "Reset"); + lv_label_set_text_static(resetButtonLabel, Translate(settingsController.GetLanguage(), StringId::Reset)); currentTripSteps = motionController.GetTripSteps(); tripLabel = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_color(tripLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_YELLOW); - lv_label_set_text_fmt(tripLabel, "Trip: %5li", currentTripSteps); + lv_label_set_text_fmt( + tripLabel, "%s: %5li", Translate(settingsController.GetLanguage(), StringId::Trip), currentTripSteps); lv_obj_align(tripLabel, lstepsGoal, LV_ALIGN_IN_LEFT_MID, 0, 20); taskRefresh = lv_task_create(RefreshTaskCallback, 100, LV_TASK_PRIO_MID, this); @@ -91,13 +94,17 @@ void Steps::Refresh() { lv_label_set_text_fmt(lSteps, "%lu", stepsCount); lv_obj_align(lSteps, nullptr, LV_ALIGN_CENTER, 0, -40); - lv_label_set_text_fmt(lStepsYesterday, yesterdayStr, motionController.NbSteps(Days::Yesterday)); + lv_label_set_text_fmt(lStepsYesterday, + "%s: %5lu", + Translate(settingsController.GetLanguage(), StringId::Yesterday), + motionController.NbSteps(Days::Yesterday)); lv_obj_align(lSteps, nullptr, LV_ALIGN_CENTER, 0, -40); if (currentTripSteps < 100000) { - lv_label_set_text_fmt(tripLabel, "Trip: %5li", currentTripSteps); + lv_label_set_text_fmt( + tripLabel, "%s: %5li", Translate(settingsController.GetLanguage(), StringId::Trip), currentTripSteps); } else { - lv_label_set_text_fmt(tripLabel, "Trip: 99999+"); + lv_label_set_text_fmt(tripLabel, "%s: 99999+", Translate(settingsController.GetLanguage(), StringId::Trip)); } lv_arc_set_value(stepsArc, int16_t(500 * stepsCount / settingsController.GetStepsGoal())); } diff --git a/src/displayapp/screens/Timer.cpp b/src/displayapp/screens/Timer.cpp index 749d985933..86cd5c8627 100644 --- a/src/displayapp/screens/Timer.cpp +++ b/src/displayapp/screens/Timer.cpp @@ -2,9 +2,11 @@ #include "displayapp/screens/Screen.h" #include "displayapp/screens/Symbols.h" #include "displayapp/InfiniTimeTheme.h" +#include "displayapp/localization/Localization.h" #include using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; static void btnEventHandler(lv_obj_t* obj, lv_event_t event) { auto* screen = static_cast(obj->user_data); @@ -17,8 +19,11 @@ static void btnEventHandler(lv_obj_t* obj, lv_event_t event) { } } -Timer::Timer(Controllers::Timer& timerController, Controllers::MotorController& motorController, System::SystemTask& systemTask) - : timer {timerController}, motorController {motorController}, wakeLock(systemTask) { +Timer::Timer(Controllers::Timer& timerController, + Controllers::Settings& settingsController, + Controllers::MotorController& motorController, + System::SystemTask& systemTask) + : timer {timerController}, settingsController {settingsController}, motorController {motorController}, wakeLock(systemTask) { lv_obj_t* colonLabel = lv_label_create(lv_scr_act(), nullptr); lv_obj_set_style_local_text_font(colonLabel, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &jetbrains_mono_76); @@ -99,7 +104,7 @@ void Timer::MaskReset() { // A click event is processed before a release event, // so the release event would override the "Pause" text without this check if (!timer.IsRunning()) { - lv_label_set_text_static(txtPlayPause, "Start"); + lv_label_set_text_static(txtPlayPause, Translate(settingsController.GetLanguage(), StringId::Start)); } maskPosition = 0; UpdateMask(); @@ -135,7 +140,7 @@ void Timer::Refresh() { } else if (timer.IsRunning()) { DisplayTime(); } else if (buttonPressing && xTaskGetTickCount() - pressTime > pdMS_TO_TICKS(150)) { - lv_label_set_text_static(txtPlayPause, "Reset"); + lv_label_set_text_static(txtPlayPause, Translate(settingsController.GetLanguage(), StringId::Reset)); maskPosition += 15; if (maskPosition > 240) { MaskReset(); @@ -158,14 +163,14 @@ void Timer::DisplayTime() { void Timer::SetTimerRunning() { minuteCounter.HideControls(); secondCounter.HideControls(); - lv_label_set_text_static(txtPlayPause, "Pause"); + lv_label_set_text_static(txtPlayPause, Translate(settingsController.GetLanguage(), StringId::Pause)); lv_obj_set_style_local_bg_color(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt); } void Timer::SetTimerStopped() { minuteCounter.ShowControls(); secondCounter.ShowControls(); - lv_label_set_text_static(txtPlayPause, "Start"); + lv_label_set_text_static(txtPlayPause, Translate(settingsController.GetLanguage(), StringId::Start)); lv_obj_set_style_local_bg_color(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GREEN); } @@ -174,7 +179,7 @@ void Timer::SetTimerRinging() { wakeLock.Lock(); minuteCounter.HideControls(); secondCounter.HideControls(); - lv_label_set_text_static(txtPlayPause, "Reset"); + lv_label_set_text_static(txtPlayPause, Translate(settingsController.GetLanguage(), StringId::Reset)); lv_obj_set_style_local_bg_color(btnPlayPause, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_RED); } diff --git a/src/displayapp/screens/Timer.h b/src/displayapp/screens/Timer.h index 651c7f0d57..a148259a3c 100644 --- a/src/displayapp/screens/Timer.h +++ b/src/displayapp/screens/Timer.h @@ -1,6 +1,7 @@ #pragma once #include "displayapp/screens/Screen.h" +#include "components/settings/Settings.h" #include "components/motor/MotorController.h" #include "systemtask/SystemTask.h" #include "systemtask/WakeLock.h" @@ -16,7 +17,10 @@ namespace Pinetime::Applications { namespace Screens { class Timer : public Screen { public: - Timer(Controllers::Timer& timerController, Controllers::MotorController& motorController, System::SystemTask& systemTask); + Timer(Controllers::Timer& timerController, + Controllers::Settings& settingsController, + Controllers::MotorController& motorController, + System::SystemTask& systemTask); ~Timer() override; void Refresh() override; void Reset(); @@ -31,6 +35,7 @@ namespace Pinetime::Applications { void UpdateMask(); void DisplayTime(); Pinetime::Controllers::Timer& timer; + Pinetime::Controllers::Settings& settingsController; Pinetime::Controllers::MotorController& motorController; Pinetime::System::WakeLock wakeLock; @@ -60,7 +65,7 @@ namespace Pinetime::Applications { static constexpr const char* icon = Screens::Symbols::hourGlass; static Screens::Screen* Create(AppControllers& controllers) { - return new Screens::Timer(controllers.timer, controllers.motorController, *controllers.systemTask); + return new Screens::Timer(controllers.timer, controllers.settingsController, controllers.motorController, *controllers.systemTask); }; static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) { diff --git a/src/displayapp/screens/Twos.cpp b/src/displayapp/screens/Twos.cpp index 6f2eff40c4..ca97e67a95 100644 --- a/src/displayapp/screens/Twos.cpp +++ b/src/displayapp/screens/Twos.cpp @@ -2,10 +2,13 @@ #include #include #include +#include "components/settings/Settings.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; -Twos::Twos() { +Twos::Twos(Controllers::Settings& settingsController) : settingsController {settingsController} { struct colorPair { lv_color_t bg; @@ -60,7 +63,7 @@ Twos::Twos() { lv_label_set_align(scoreText, LV_ALIGN_IN_LEFT_MID); lv_obj_align(scoreText, nullptr, LV_ALIGN_IN_TOP_LEFT, 0, 0); lv_label_set_recolor(scoreText, true); - lv_label_set_text_fmt(scoreText, "Score #FFFF00 %i#", score); + lv_label_set_text_fmt(scoreText, "%s #FFFF00 %i#", Translate(settingsController.GetLanguage(), StringId::Score), score); } Twos::~Twos() { @@ -103,7 +106,7 @@ bool Twos::tryMerge(int newRow, int newCol, int oldRow, int oldCol) { if (!grid[newRow][newCol].merged) { grid[newRow][newCol].value *= 2; score += grid[newRow][newCol].value; - lv_label_set_text_fmt(scoreText, "Score #FFFF00 %i#", score); + lv_label_set_text_fmt(scoreText, "%s #FFFF00 %i#", Translate(settingsController.GetLanguage(), StringId::Score), score); grid[oldRow][oldCol].value = 0; grid[newRow][newCol].merged = true; return true; diff --git a/src/displayapp/screens/Twos.h b/src/displayapp/screens/Twos.h index 9ebd7f2e44..b5205ec887 100644 --- a/src/displayapp/screens/Twos.h +++ b/src/displayapp/screens/Twos.h @@ -14,7 +14,7 @@ namespace Pinetime { namespace Screens { class Twos : public Screen { public: - Twos(); + explicit Twos(Controllers::Settings& settingsController); ~Twos() override; bool OnTouchEvent(TouchEvents event) override; @@ -29,6 +29,7 @@ namespace Pinetime { static constexpr int nRows = 4; static constexpr int nCells = nCols * nRows; TwosTile grid[nRows][nCols]; + Controllers::Settings& settingsController; unsigned int score = 0; void updateGridDisplay(); bool tryMerge(int newRow, int newCol, int oldRow, int oldCol); @@ -42,8 +43,8 @@ namespace Pinetime { static constexpr Apps app = Apps::Twos; static constexpr const char* icon = "2"; - static Screens::Screen* Create(AppControllers& /*controllers*/) { - return new Screens::Twos(); + static Screens::Screen* Create(AppControllers& controllers) { + return new Screens::Twos(controllers.settingsController); }; static bool IsAvailable(Pinetime::Controllers::FS& /*filesystem*/) { diff --git a/src/displayapp/screens/WatchFaceInfineat.cpp b/src/displayapp/screens/WatchFaceInfineat.cpp index c793971103..378cee20fe 100644 --- a/src/displayapp/screens/WatchFaceInfineat.cpp +++ b/src/displayapp/screens/WatchFaceInfineat.cpp @@ -427,7 +427,10 @@ void WatchFaceInfineat::Refresh() { if (currentDate.IsUpdated()) { uint8_t day = dateTimeController.Day(); Controllers::DateTime::Days dayOfWeek = dateTimeController.DayOfWeek(); - lv_label_set_text_fmt(labelDate, "%s %02d", dateTimeController.DayOfWeekShortToStringLow(dayOfWeek), day); + lv_label_set_text_fmt(labelDate, + "%s %02d", + dateTimeController.DayOfWeekShortToStringLow(dayOfWeek, settingsController.GetLanguage()), + day); lv_obj_realign(labelDate); } } diff --git a/src/displayapp/screens/WatchFacePrideFlag.cpp b/src/displayapp/screens/WatchFacePrideFlag.cpp index e029c076f4..aa95a9f9fd 100644 --- a/src/displayapp/screens/WatchFacePrideFlag.cpp +++ b/src/displayapp/screens/WatchFacePrideFlag.cpp @@ -2,9 +2,11 @@ #include #include "components/battery/BatteryController.h" #include "components/ble/BleController.h" +#include "displayapp/localization/Localization.h" #include "displayapp/screens/Symbols.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { void EventHandler(lv_obj_t* obj, lv_event_t event) { @@ -198,9 +200,13 @@ void WatchFacePrideFlag::Refresh() { bleState = bleController.IsConnected(); batteryPercentRemaining = batteryController.PercentRemaining(); if (batteryPercentRemaining.IsUpdated() || powerPresent.IsUpdated() || themeChanged) { - lv_label_set_text_fmt(batteryValue, "%d%%", batteryPercentRemaining.Get()); if (batteryController.IsPowerPresent()) { - lv_label_ins_text(batteryValue, LV_LABEL_POS_LAST, " Charging"); + lv_label_set_text_fmt(batteryValue, + "%d%% %s", + batteryPercentRemaining.Get(), + Translate(settingsController.GetLanguage(), StringId::Charging)); + } else { + lv_label_set_text_fmt(batteryValue, "%d%%", batteryPercentRemaining.Get()); } } if (bleState.IsUpdated()) { @@ -214,7 +220,7 @@ void WatchFacePrideFlag::Refresh() { notificationState = notificationManager.AreNewNotificationsAvailable(); if (notificationState.IsUpdated()) { if (notificationState.Get()) { - lv_label_set_text_static(notificationText, "You have\nmail!"); + lv_label_set_text_static(notificationText, Translate(settingsController.GetLanguage(), StringId::YouHaveMail)); } else { lv_label_set_text_static(notificationText, ""); } @@ -246,18 +252,19 @@ void WatchFacePrideFlag::Refresh() { const Controllers::DateTime::Months month = dateTimeController.Month(); const uint8_t day = dateTimeController.Day(); const Controllers::DateTime::Days dayOfWeek = dateTimeController.DayOfWeek(); + const auto language = settingsController.GetLanguage(); lv_label_set_text_fmt(labelDate, "%02d-%02d-%04d", day, static_cast(month), year); if (settingsController.GetClockType() == Controllers::Settings::ClockType::H12) { - lv_label_set_text_fmt(labelDay, "%s %s", dateTimeController.DayOfWeekToStringLow(dayOfWeek), ampmChar); + lv_label_set_text_fmt(labelDay, "%s %s", dateTimeController.DayOfWeekToStringLow(dayOfWeek, language), ampmChar); } else { - lv_label_set_text_fmt(labelDay, "%s", dateTimeController.DayOfWeekToStringLow(dayOfWeek)); + lv_label_set_text_fmt(labelDay, "%s", dateTimeController.DayOfWeekToStringLow(dayOfWeek, language)); } } } stepCount = motionController.NbSteps(); if (stepCount.IsUpdated() || themeChanged) { - lv_label_set_text_fmt(stepValue, "%lu steps", stepCount.Get()); + lv_label_set_text_fmt(stepValue, "%lu %s", stepCount.Get(), Translate(settingsController.GetLanguage(), StringId::Steps)); } if (themeChanged) { themeChanged = false; diff --git a/src/displayapp/screens/WatchFaceTerminal.cpp b/src/displayapp/screens/WatchFaceTerminal.cpp index 505f79500c..8589d868f2 100644 --- a/src/displayapp/screens/WatchFaceTerminal.cpp +++ b/src/displayapp/screens/WatchFaceTerminal.cpp @@ -8,10 +8,12 @@ #include "components/motion/MotionController.h" #include "components/settings/Settings.h" #include "components/ble/SimpleWeatherService.h" +#include "displayapp/localization/Localization.h" #include "displayapp/screens/WeatherSymbols.h" #include "displayapp/InfiniTimeTheme.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; WatchFaceTerminal::WatchFaceTerminal(Controllers::DateTime& dateTimeController, const Controllers::Battery& batteryController, @@ -127,15 +129,19 @@ void WatchFaceTerminal::Refresh() { LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, BatteryIcon::ColorFromPercentage(batteryPercentRemaining.Get())); - lv_label_set_text_fmt(batteryValue, "#ffffff [BATT]# %d%%", batteryPercentRemaining.Get()); if (batteryController.IsCharging()) { - lv_label_ins_text(batteryValue, LV_LABEL_POS_LAST, " Charging"); + lv_label_set_text_fmt(batteryValue, + "#ffffff [BATT]# %d%% %s", + batteryPercentRemaining.Get(), + Translate(settingsController.GetLanguage(), StringId::Charging)); + } else { + lv_label_set_text_fmt(batteryValue, "#ffffff [BATT]# %d%%", batteryPercentRemaining.Get()); } } stepCount = motionController.NbSteps(); if (stepCount.IsUpdated()) { - lv_label_set_text_fmt(stepValue, "#ffffff [STEP]# %lu steps", stepCount.Get()); + lv_label_set_text_fmt(stepValue, "#ffffff [STEP]# %lu %s", stepCount.Get(), Translate(settingsController.GetLanguage(), StringId::Steps)); } heartbeat = heartRateController.HeartRate(); @@ -164,7 +170,7 @@ void WatchFaceTerminal::Refresh() { "#ffffff [WTHR]# #ffdd00 %d°%c %s#", temp, tempUnit, - Symbols::GetSimpleCondition(optCurrentWeather->iconId)); + Symbols::GetSimpleCondition(optCurrentWeather->iconId, settingsController.GetLanguage())); } else { lv_label_set_text(weather, "#ffffff [WTHR]# #ffdd00 ---"); } @@ -174,14 +180,14 @@ void WatchFaceTerminal::Refresh() { bleRadioEnabled = bleController.IsRadioEnabled(); if (bleState.IsUpdated() || bleRadioEnabled.IsUpdated()) { if (!bleRadioEnabled.Get()) { - lv_label_set_text_static(connectState, "#ffffff [STAT]# Disabled"); + lv_label_set_text_fmt(connectState, "#ffffff [STAT]# %s", Translate(settingsController.GetLanguage(), StringId::Disabled)); lv_obj_set_style_local_text_color(connectState, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::gray); } else { if (bleState.Get()) { - lv_label_set_text_static(connectState, "#ffffff [STAT]# Connected"); + lv_label_set_text_fmt(connectState, "#ffffff [STAT]# %s", Translate(settingsController.GetLanguage(), StringId::Connected)); lv_obj_set_style_local_text_color(connectState, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::blue); } else { - lv_label_set_text_static(connectState, "#ffffff [STAT]# Disconnected"); + lv_label_set_text_fmt(connectState, "#ffffff [STAT]# %s", Translate(settingsController.GetLanguage(), StringId::Disconnected)); lv_obj_set_style_local_text_color(connectState, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, Colors::gray); } } diff --git a/src/displayapp/screens/Weather.cpp b/src/displayapp/screens/Weather.cpp index de32a1538b..ad2fff4fab 100644 --- a/src/displayapp/screens/Weather.cpp +++ b/src/displayapp/screens/Weather.cpp @@ -119,7 +119,7 @@ void Weather::Refresh() { } lv_obj_set_style_local_text_color(temperature, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, optCurrentWeather->temperature.Color()); lv_label_set_text(icon, Symbols::GetSymbol(optCurrentWeather->iconId, weatherService.IsNight())); - lv_label_set_text(condition, Symbols::GetCondition(optCurrentWeather->iconId)); + lv_label_set_text(condition, Symbols::GetCondition(optCurrentWeather->iconId, settingsController.GetLanguage())); lv_label_set_text_fmt(temperature, "%d°%c", temp, tempUnit); lv_label_set_text_fmt(minTemperature, "%d°", minTemp); lv_label_set_text_fmt(maxTemperature, "%d°", maxTemp); @@ -152,7 +152,8 @@ void Weather::Refresh() { if (wday > 7) { wday -= 7; } - const char* dayOfWeek = Controllers::DateTime::DayOfWeekShortToStringLow(static_cast(wday)); + const char* dayOfWeek = Controllers::DateTime::DayOfWeekShortToStringLow(static_cast(wday), + settingsController.GetLanguage()); lv_table_set_cell_value(forecast, 0, i, dayOfWeek); lv_table_set_cell_value(forecast, 1, i, Symbols::GetSymbol(optCurrentForecast->days[i]->iconId, false)); // Pad cells based on the largest number of digits on each column diff --git a/src/displayapp/screens/WeatherSymbols.cpp b/src/displayapp/screens/WeatherSymbols.cpp index 762180c75c..73e9c83410 100644 --- a/src/displayapp/screens/WeatherSymbols.cpp +++ b/src/displayapp/screens/WeatherSymbols.cpp @@ -1,4 +1,7 @@ #include "displayapp/screens/WeatherSymbols.h" +#include "displayapp/localization/Localization.h" + +using namespace Pinetime::Applications::Localization; const char* Pinetime::Applications::Screens::Symbols::GetSymbol(const Pinetime::Controllers::SimpleWeatherService::Icons icon, const bool isNight) { @@ -45,49 +48,51 @@ const char* Pinetime::Applications::Screens::Symbols::GetSymbol(const Pinetime:: } } -const char* Pinetime::Applications::Screens::Symbols::GetCondition(const Pinetime::Controllers::SimpleWeatherService::Icons icon) { +const char* Pinetime::Applications::Screens::Symbols::GetCondition(const Pinetime::Controllers::SimpleWeatherService::Icons icon, + Language language) { switch (icon) { case Pinetime::Controllers::SimpleWeatherService::Icons::Sun: - return "Clear sky"; + return Translate(language, StringId::ClearSky); case Pinetime::Controllers::SimpleWeatherService::Icons::CloudsSun: - return "Few clouds"; + return Translate(language, StringId::FewClouds); case Pinetime::Controllers::SimpleWeatherService::Icons::Clouds: - return "Scattered clouds"; + return Translate(language, StringId::ScatteredClouds); case Pinetime::Controllers::SimpleWeatherService::Icons::BrokenClouds: - return "Broken clouds"; + return Translate(language, StringId::BrokenClouds); case Pinetime::Controllers::SimpleWeatherService::Icons::CloudShowerHeavy: - return "Shower rain"; + return Translate(language, StringId::ShowerRain); case Pinetime::Controllers::SimpleWeatherService::Icons::CloudSunRain: - return "Rain"; + return Translate(language, StringId::Rain); case Pinetime::Controllers::SimpleWeatherService::Icons::Thunderstorm: - return "Thunderstorm"; + return Translate(language, StringId::Thunderstorm); case Pinetime::Controllers::SimpleWeatherService::Icons::Snow: - return "Snow"; + return Translate(language, StringId::Snow); case Pinetime::Controllers::SimpleWeatherService::Icons::Smog: - return "Mist"; + return Translate(language, StringId::Mist); default: return ""; } } -const char* Pinetime::Applications::Screens::Symbols::GetSimpleCondition(const Pinetime::Controllers::SimpleWeatherService::Icons icon) { +const char* Pinetime::Applications::Screens::Symbols::GetSimpleCondition(const Pinetime::Controllers::SimpleWeatherService::Icons icon, + Language language) { switch (icon) { case Pinetime::Controllers::SimpleWeatherService::Icons::Sun: case Pinetime::Controllers::SimpleWeatherService::Icons::CloudsSun: - return "Clear"; + return Translate(language, StringId::Clear); case Pinetime::Controllers::SimpleWeatherService::Icons::Clouds: case Pinetime::Controllers::SimpleWeatherService::Icons::BrokenClouds: - return "Cloudy"; + return Translate(language, StringId::Cloudy); case Pinetime::Controllers::SimpleWeatherService::Icons::CloudShowerHeavy: - return "Showers"; + return Translate(language, StringId::Showers); case Pinetime::Controllers::SimpleWeatherService::Icons::CloudSunRain: - return "Rain"; + return Translate(language, StringId::Rain); case Pinetime::Controllers::SimpleWeatherService::Icons::Thunderstorm: - return "Thunder"; + return Translate(language, StringId::Thunder); case Pinetime::Controllers::SimpleWeatherService::Icons::Snow: - return "Snow"; + return Translate(language, StringId::Snow); case Pinetime::Controllers::SimpleWeatherService::Icons::Smog: - return "Mist"; + return Translate(language, StringId::Mist); default: return ""; } diff --git a/src/displayapp/screens/WeatherSymbols.h b/src/displayapp/screens/WeatherSymbols.h index 15c8a8a360..e41192e007 100644 --- a/src/displayapp/screens/WeatherSymbols.h +++ b/src/displayapp/screens/WeatherSymbols.h @@ -1,5 +1,6 @@ #pragma once #include "components/ble/SimpleWeatherService.h" +#include "displayapp/localization/Localization.h" #include "displayapp/screens/Symbols.h" namespace Pinetime { @@ -7,8 +8,10 @@ namespace Pinetime { namespace Screens { namespace Symbols { const char* GetSymbol(const Pinetime::Controllers::SimpleWeatherService::Icons icon, const bool isNight); - const char* GetCondition(const Pinetime::Controllers::SimpleWeatherService::Icons icon); - const char* GetSimpleCondition(const Pinetime::Controllers::SimpleWeatherService::Icons icon); + const char* GetCondition(const Pinetime::Controllers::SimpleWeatherService::Icons icon, + Pinetime::Applications::Localization::Language language); + const char* GetSimpleCondition(const Pinetime::Controllers::SimpleWeatherService::Icons icon, + Pinetime::Applications::Localization::Language language); } } } diff --git a/src/displayapp/screens/settings/SettingBluetooth.cpp b/src/displayapp/screens/settings/SettingBluetooth.cpp index e4dc695c94..86521f7173 100644 --- a/src/displayapp/screens/settings/SettingBluetooth.cpp +++ b/src/displayapp/screens/settings/SettingBluetooth.cpp @@ -5,28 +5,30 @@ #include "displayapp/screens/Styles.h" #include "displayapp/screens/Screen.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { struct Option { - const char* name; + StringId name; bool radioEnabled; }; constexpr std::array options = {{ - {"Enabled", true}, - {"Disabled", false}, + {StringId::Enabled, true}, + {StringId::Disabled, false}, }}; - std::array CreateOptionArray() { + std::array CreateOptionArray(Language language) { std::array optionArray; for (size_t i = 0; i < CheckboxList::MaxItems; i++) { if (i >= options.size()) { optionArray[i].name = ""; optionArray[i].enabled = false; } else { - optionArray[i].name = options[i].name; + optionArray[i].name = Translate(language, options[i].name); optionArray[i].enabled = true; } } @@ -40,7 +42,7 @@ SettingBluetooth::SettingBluetooth(Pinetime::Applications::DisplayApp* app, Pine checkboxList( 0, 1, - "Bluetooth", + Translate(settingsController.GetLanguage(), StringId::Bluetooth), Symbols::bluetooth, settingsController.GetBleRadioEnabled() ? 0 : 1, [this](uint32_t index) { @@ -51,7 +53,7 @@ SettingBluetooth::SettingBluetooth(Pinetime::Applications::DisplayApp* app, Pine this->app->PushMessage(Pinetime::Applications::Display::Messages::BleRadioEnableToggle); } }, - CreateOptionArray()) { + CreateOptionArray(settingsController.GetLanguage())) { } SettingBluetooth::~SettingBluetooth() { diff --git a/src/displayapp/screens/settings/SettingChimes.cpp b/src/displayapp/screens/settings/SettingChimes.cpp index 2d3473debf..7511c394a2 100644 --- a/src/displayapp/screens/settings/SettingChimes.cpp +++ b/src/displayapp/screens/settings/SettingChimes.cpp @@ -4,30 +4,32 @@ #include "displayapp/screens/Styles.h" #include "displayapp/screens/Screen.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/localization/Localization.h" #include using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { struct Option { Pinetime::Controllers::Settings::ChimesOption chimesOption; - const char* name; + StringId name; }; constexpr std::array options = {{ - {Pinetime::Controllers::Settings::ChimesOption::None, "Off"}, - {Pinetime::Controllers::Settings::ChimesOption::Hours, "Every hour"}, - {Pinetime::Controllers::Settings::ChimesOption::HalfHours, "Every 30 mins"}, + {Pinetime::Controllers::Settings::ChimesOption::None, StringId::Off}, + {Pinetime::Controllers::Settings::ChimesOption::Hours, StringId::EveryHour}, + {Pinetime::Controllers::Settings::ChimesOption::HalfHours, StringId::Every30Minutes}, }}; - std::array CreateOptionArray() { + std::array CreateOptionArray(Language language) { std::array optionArray; for (size_t i = 0; i < CheckboxList::MaxItems; i++) { if (i >= options.size()) { optionArray[i].name = ""; optionArray[i].enabled = false; } else { - optionArray[i].name = options[i].name; + optionArray[i].name = Translate(language, options[i].name); optionArray[i].enabled = true; } } @@ -48,14 +50,14 @@ SettingChimes::SettingChimes(Pinetime::Controllers::Settings& settingsController : checkboxList( 0, 1, - "Chimes", + Translate(settingsController.GetLanguage(), StringId::Chimes), Symbols::clock, GetDefaultOption(settingsController.GetChimeOption()), [&settings = settingsController](uint32_t index) { settings.SetChimeOption(options[index].chimesOption); settings.SaveSettings(); }, - CreateOptionArray()) { + CreateOptionArray(settingsController.GetLanguage())) { } SettingChimes::~SettingChimes() { diff --git a/src/displayapp/screens/settings/SettingDisplay.cpp b/src/displayapp/screens/settings/SettingDisplay.cpp index bbc188a9d7..bfd52177d2 100644 --- a/src/displayapp/screens/settings/SettingDisplay.cpp +++ b/src/displayapp/screens/settings/SettingDisplay.cpp @@ -5,8 +5,10 @@ #include "displayapp/screens/Styles.h" #include "displayapp/screens/Screen.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { void TimeoutEventHandler(lv_obj_t* obj, lv_event_t event) { @@ -39,7 +41,7 @@ SettingDisplay::SettingDisplay(Pinetime::Controllers::Settings& settingsControll lv_cont_set_layout(container1, LV_LAYOUT_PRETTY_TOP); lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(title, "Display timeout"); + lv_label_set_text_static(title, Translate(settingsController.GetLanguage(), StringId::DisplayTimeout)); lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 10, 15); @@ -64,7 +66,7 @@ SettingDisplay::SettingDisplay(Pinetime::Controllers::Settings& settingsControll } alwaysOnCheckbox = lv_checkbox_create(container1, nullptr); - lv_checkbox_set_text(alwaysOnCheckbox, "Always On"); + lv_checkbox_set_text(alwaysOnCheckbox, Translate(settingsController.GetLanguage(), StringId::AlwaysOn)); lv_checkbox_set_checked(alwaysOnCheckbox, settingsController.GetAlwaysOnDisplaySetting()); lv_obj_add_state(alwaysOnCheckbox, LV_STATE_DEFAULT); alwaysOnCheckbox->user_data = this; diff --git a/src/displayapp/screens/settings/SettingHeartRate.cpp b/src/displayapp/screens/settings/SettingHeartRate.cpp index a45dc835ae..b4c18093d1 100644 --- a/src/displayapp/screens/settings/SettingHeartRate.cpp +++ b/src/displayapp/screens/settings/SettingHeartRate.cpp @@ -2,14 +2,35 @@ #include #include "displayapp/screens/Styles.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { void EventHandler(lv_obj_t* obj, lv_event_t event) { auto* screen = static_cast(obj->user_data); screen->UpdateSelected(obj, event); } + + const char* IntervalLabel(std::optional interval) { + if (interval == 30) { + return " 30s"; + } + if (interval == 60) { + return " 1m"; + } + if (interval == 5 * 60) { + return " 5m"; + } + if (interval == 10 * 60) { + return " 10m"; + } + if (interval == 30 * 60) { + return " 30m"; + } + return ""; + } } SettingHeartRate::SettingHeartRate(Pinetime::Controllers::Settings& settingsController) : settingsController {settingsController} { @@ -26,8 +47,7 @@ SettingHeartRate::SettingHeartRate(Pinetime::Controllers::Settings& settingsCont lv_cont_set_layout(container, LV_LAYOUT_PRETTY_TOP); lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(title, "Backg. Interval"); - lv_label_set_text(title, "Backg. Interval"); + lv_label_set_text_static(title, Translate(settingsController.GetLanguage(), StringId::BackgroundInterval)); lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 10, 15); @@ -41,7 +61,13 @@ SettingHeartRate::SettingHeartRate(Pinetime::Controllers::Settings& settingsCont for (std::size_t i = 0; i < options.size(); i++) { cbOption[i] = lv_checkbox_create(container, nullptr); - lv_checkbox_set_text(cbOption[i], options[i].name); + if (options[i].intervalInSeconds == std::nullopt) { + lv_checkbox_set_text(cbOption[i], Translate(settingsController.GetLanguage(), StringId::Off)); + } else if (options[i].intervalInSeconds == 0) { + lv_checkbox_set_text(cbOption[i], Translate(settingsController.GetLanguage(), StringId::Continuous)); + } else { + lv_checkbox_set_text(cbOption[i], IntervalLabel(options[i].intervalInSeconds)); + } cbOption[i]->user_data = this; lv_obj_set_event_cb(cbOption[i], EventHandler); SetRadioButtonStyle(cbOption[i]); diff --git a/src/displayapp/screens/settings/SettingHeartRate.h b/src/displayapp/screens/settings/SettingHeartRate.h index 736f2b10d7..a424c3dd54 100644 --- a/src/displayapp/screens/settings/SettingHeartRate.h +++ b/src/displayapp/screens/settings/SettingHeartRate.h @@ -6,6 +6,7 @@ #include #include "components/settings/Settings.h" +#include "displayapp/localization/Localization.h" #include "displayapp/screens/Screen.h" namespace Pinetime { @@ -22,19 +23,19 @@ namespace Pinetime { private: struct Option { std::optional intervalInSeconds; - const char* name; + Pinetime::Applications::Localization::StringId name; }; Pinetime::Controllers::Settings& settingsController; static constexpr std::array options = {{ - {.intervalInSeconds = std::nullopt, .name = " Off"}, - {.intervalInSeconds = 0, .name = "Cont"}, - {.intervalInSeconds = 30, .name = " 30s"}, - {.intervalInSeconds = 60, .name = " 1m"}, - {.intervalInSeconds = 5 * 60, .name = " 5m"}, - {.intervalInSeconds = 10 * 60, .name = " 10m"}, - {.intervalInSeconds = 30 * 60, .name = " 30m"}, + {.intervalInSeconds = std::nullopt, .name = Pinetime::Applications::Localization::StringId::Off}, + {.intervalInSeconds = 0, .name = Pinetime::Applications::Localization::StringId::Continuous}, + {.intervalInSeconds = 30, .name = Pinetime::Applications::Localization::StringId::Empty}, + {.intervalInSeconds = 60, .name = Pinetime::Applications::Localization::StringId::Empty}, + {.intervalInSeconds = 5 * 60, .name = Pinetime::Applications::Localization::StringId::Empty}, + {.intervalInSeconds = 10 * 60, .name = Pinetime::Applications::Localization::StringId::Empty}, + {.intervalInSeconds = 30 * 60, .name = Pinetime::Applications::Localization::StringId::Empty}, }}; lv_obj_t* cbOption[options.size()]; diff --git a/src/displayapp/screens/settings/SettingLanguage.cpp b/src/displayapp/screens/settings/SettingLanguage.cpp new file mode 100644 index 0000000000..9848d51bd9 --- /dev/null +++ b/src/displayapp/screens/settings/SettingLanguage.cpp @@ -0,0 +1,122 @@ +#include "displayapp/screens/settings/SettingLanguage.h" + +#include +#include +#include + +#include "displayapp/localization/Localization.h" +#include "displayapp/screens/Symbols.h" + +using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; + +namespace { + struct Option { + Language language; + StringId name; + }; + + constexpr std::array options {{ + {Language::English, StringId::English}, + {Language::Spanish, StringId::Spanish}, + {Language::Portuguese, StringId::Portuguese}, + {Language::Russian, StringId::Russian}, + {Language::French, StringId::French}, + {Language::German, StringId::German}, + {Language::Italian, StringId::Italian}, + {Language::Turkish, StringId::Turkish}, + {Language::Polish, StringId::Polish}, + {Language::Catalan, StringId::Catalan}, + {Language::Basque, StringId::Basque}, + }}; + + std::array CreateOptionArray(Language language, uint8_t screenId) { + std::array optionArray; + for (size_t i = 0; i < CheckboxList::MaxItems; i++) { + const auto optionIndex = i + (screenId * CheckboxList::MaxItems); + if (optionIndex >= options.size()) { + optionArray[i].name = ""; + optionArray[i].enabled = false; + } else { + optionArray[i].name = Translate(language, options[optionIndex].name); + optionArray[i].enabled = true; + } + } + return optionArray; + } + + uint32_t GetDefaultOption(Language currentOption) { + for (size_t i = 0; i < options.size(); i++) { + if (options[i].language == currentOption) { + return i; + } + } + return 0; + } + + class LanguagePage : public Screen { + public: + LanguagePage(uint8_t screenNum, uint8_t numScreens, Pinetime::Controllers::Settings& settingsController) + : settingsController {settingsController}, + screenNum {screenNum}, + checkboxList( + screenNum, + numScreens, + Translate(settingsController.GetLanguage(), StringId::Language), + Symbols::map, + GetDefaultOption(settingsController.GetLanguage()), + [this](uint32_t index) { + if (index >= options.size()) { + return; + } + const auto language = options[index].language; + this->settingsController.SetLanguage(language); + this->settingsController.SaveSettings(); + UpdateScreen(language); + }, + CreateOptionArray(settingsController.GetLanguage(), screenNum)) { + } + + ~LanguagePage() override { + lv_obj_clean(lv_scr_act()); + } + + private: + void UpdateScreen(Language language) { + checkboxList.SetTitle(Translate(language, StringId::Language)); + checkboxList.SetSymbol(Symbols::map); + checkboxList.SetOptions(CreateOptionArray(language, screenNum)); + } + + Pinetime::Controllers::Settings& settingsController; + uint8_t screenNum; + CheckboxList checkboxList; + }; +} + +auto SettingLanguage::CreateScreenList() const { + std::array()>, nScreens> screenList; + for (size_t i = 0; i < screenList.size(); i++) { + screenList[i] = [this, i]() -> std::unique_ptr { + return CreateScreen(i); + }; + } + return screenList; +} + +std::unique_ptr SettingLanguage::CreateScreen(unsigned int screenNum) const { + return std::make_unique(screenNum, nScreens, settingsController); +} + +SettingLanguage::SettingLanguage(DisplayApp* app, Pinetime::Controllers::Settings& settingsController) + : settingsController {settingsController}, + screens {app, static_cast(GetDefaultOption(settingsController.GetLanguage()) / CheckboxList::MaxItems), CreateScreenList(), Screens::ScreenListModes::UpDown} { +} + +SettingLanguage::~SettingLanguage() { + lv_obj_clean(lv_scr_act()); +} + +bool SettingLanguage::OnTouchEvent(TouchEvents event) { + return screens.OnTouchEvent(event); +} diff --git a/src/displayapp/screens/settings/SettingLanguage.h b/src/displayapp/screens/settings/SettingLanguage.h new file mode 100644 index 0000000000..967701a904 --- /dev/null +++ b/src/displayapp/screens/settings/SettingLanguage.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include "displayapp/screens/CheckboxList.h" +#include "displayapp/screens/ScreenList.h" +#include "displayapp/DisplayApp.h" +#include "displayapp/localization/Localization.h" +#include "components/settings/Settings.h" + +namespace Pinetime { + namespace Applications { + namespace Screens { + + class SettingLanguage : public Screen { + public: + SettingLanguage(DisplayApp* app, Pinetime::Controllers::Settings& settingsController); + ~SettingLanguage() override; + + bool OnTouchEvent(TouchEvents event) override; + + private: + auto CreateScreenList() const; + std::unique_ptr CreateScreen(unsigned int screenNum) const; + + static constexpr int languagesPerScreen = CheckboxList::MaxItems; + static constexpr int nScreens = (static_cast(Pinetime::Applications::Localization::Language::Count) - 1) / languagesPerScreen + 1; + + Pinetime::Controllers::Settings& settingsController; + ScreenList screens; + }; + + } + } +} diff --git a/src/displayapp/screens/settings/SettingOTA.cpp b/src/displayapp/screens/settings/SettingOTA.cpp index ad123dec51..84065a42e7 100644 --- a/src/displayapp/screens/settings/SettingOTA.cpp +++ b/src/displayapp/screens/settings/SettingOTA.cpp @@ -5,29 +5,31 @@ #include "displayapp/screens/Styles.h" #include "displayapp/screens/Screen.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { struct Option { - const char* name; + StringId name; Pinetime::Controllers::Settings::DfuAndFsMode mode; }; constexpr std::array options = {{ - {"Enabled", Pinetime::Controllers::Settings::DfuAndFsMode::Enabled}, - {"Disabled", Pinetime::Controllers::Settings::DfuAndFsMode::Disabled}, - {"Till reboot", Pinetime::Controllers::Settings::DfuAndFsMode::EnabledTillReboot}, + {StringId::Enabled, Pinetime::Controllers::Settings::DfuAndFsMode::Enabled}, + {StringId::Disabled, Pinetime::Controllers::Settings::DfuAndFsMode::Disabled}, + {StringId::TillReboot, Pinetime::Controllers::Settings::DfuAndFsMode::EnabledTillReboot}, }}; - std::array CreateOptionArray() { + std::array CreateOptionArray(Language language) { std::array optionArray; for (size_t i = 0; i < CheckboxList::MaxItems; i++) { if (i >= options.size()) { optionArray[i].name = ""; optionArray[i].enabled = false; } else { - optionArray[i].name = options[i].name; + optionArray[i].name = Translate(language, options[i].name); optionArray[i].enabled = true; } } @@ -41,7 +43,7 @@ SettingOTA::SettingOTA(Pinetime::Applications::DisplayApp* app, Pinetime::Contro checkboxList( 0, 1, - "Firmware & files", + Translate(settingsController.GetLanguage(), StringId::FirmwareAndFiles), Symbols::shieldAlt, settingsController.GetDfuAndFsMode() == Pinetime::Controllers::Settings::DfuAndFsMode::Enabled ? 0 : settingsController.GetDfuAndFsMode() == Pinetime::Controllers::Settings::DfuAndFsMode::EnabledTillReboot ? 2 @@ -49,7 +51,7 @@ SettingOTA::SettingOTA(Pinetime::Applications::DisplayApp* app, Pinetime::Contro [&settings = settingsController](uint32_t index) { settings.SetDfuAndFsMode(options[index].mode); }, - CreateOptionArray()) { + CreateOptionArray(settingsController.GetLanguage())) { } SettingOTA::~SettingOTA() { diff --git a/src/displayapp/screens/settings/SettingSetDate.cpp b/src/displayapp/screens/settings/SettingSetDate.cpp index 93a8da4d27..e5915dfa63 100644 --- a/src/displayapp/screens/settings/SettingSetDate.cpp +++ b/src/displayapp/screens/settings/SettingSetDate.cpp @@ -4,8 +4,10 @@ #include #include "displayapp/DisplayApp.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { constexpr int16_t POS_X_DAY = -72; @@ -46,11 +48,12 @@ namespace { } SettingSetDate::SettingSetDate(Pinetime::Controllers::DateTime& dateTimeController, + Pinetime::Controllers::Settings& settingsController, Pinetime::Applications::Screens::SettingSetDateTime& settingSetDateTime) - : dateTimeController {dateTimeController}, settingSetDateTime {settingSetDateTime} { + : dateTimeController {dateTimeController}, settingsController {settingsController}, settingSetDateTime {settingSetDateTime} { lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(title, "Set current date"); + lv_label_set_text_static(title, Translate(settingsController.GetLanguage(), StringId::SetCurrentDate)); lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 15, 15); @@ -66,7 +69,7 @@ SettingSetDate::SettingSetDate(Pinetime::Controllers::DateTime& dateTimeControll dayCounter.SetValue(dateTimeController.Day()); lv_obj_align(dayCounter.GetObject(), nullptr, LV_ALIGN_CENTER, POS_X_DAY, POS_Y_TEXT); - monthCounter.EnableMonthMode(); + monthCounter.EnableMonthMode(settingsController.GetLanguage()); monthCounter.SetValueChangedEventCallback(this, ValueChangedHandler); monthCounter.Create(); monthCounter.SetValue(static_cast(dateTimeController.Month())); @@ -83,7 +86,7 @@ SettingSetDate::SettingSetDate(Pinetime::Controllers::DateTime& dateTimeControll lv_obj_align(btnSetTime, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0); lv_obj_set_style_local_bg_color(btnSetTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_MAKE(0x38, 0x38, 0x38)); lblSetTime = lv_label_create(btnSetTime, nullptr); - lv_label_set_text_static(lblSetTime, "Set"); + lv_label_set_text_static(lblSetTime, Translate(settingsController.GetLanguage(), StringId::Set)); lv_obj_set_event_cb(btnSetTime, event_handler); } diff --git a/src/displayapp/screens/settings/SettingSetDate.h b/src/displayapp/screens/settings/SettingSetDate.h index 1fe2bd9eb4..2d8596e174 100644 --- a/src/displayapp/screens/settings/SettingSetDate.h +++ b/src/displayapp/screens/settings/SettingSetDate.h @@ -3,6 +3,7 @@ #include #include #include "components/datetime/DateTimeController.h" +#include "components/settings/Settings.h" #include "displayapp/screens/Screen.h" #include "displayapp/widgets/Counter.h" #include "displayapp/widgets/DotIndicator.h" @@ -14,6 +15,7 @@ namespace Pinetime { class SettingSetDate : public Screen { public: SettingSetDate(Pinetime::Controllers::DateTime& dateTimeController, + Pinetime::Controllers::Settings& settingsController, Pinetime::Applications::Screens::SettingSetDateTime& settingSetDateTime); ~SettingSetDate() override; @@ -22,6 +24,7 @@ namespace Pinetime { private: Controllers::DateTime& dateTimeController; + Controllers::Settings& settingsController; Pinetime::Applications::Screens::SettingSetDateTime& settingSetDateTime; lv_obj_t* btnSetTime; diff --git a/src/displayapp/screens/settings/SettingSetDateTime.cpp b/src/displayapp/screens/settings/SettingSetDateTime.cpp index 8926ff31b1..24937dc157 100644 --- a/src/displayapp/screens/settings/SettingSetDateTime.cpp +++ b/src/displayapp/screens/settings/SettingSetDateTime.cpp @@ -31,7 +31,7 @@ SettingSetDateTime::SettingSetDateTime(Pinetime::Applications::DisplayApp* app, std::unique_ptr SettingSetDateTime::screenSetDate() { Widgets::DotIndicator dotIndicator(0, 2); dotIndicator.Create(); - return std::make_unique(dateTimeController, *this); + return std::make_unique(dateTimeController, settingsController, *this); } std::unique_ptr SettingSetDateTime::screenSetTime() { diff --git a/src/displayapp/screens/settings/SettingSetTime.cpp b/src/displayapp/screens/settings/SettingSetTime.cpp index e5a6be2f9b..3330f78751 100644 --- a/src/displayapp/screens/settings/SettingSetTime.cpp +++ b/src/displayapp/screens/settings/SettingSetTime.cpp @@ -5,8 +5,10 @@ #include "displayapp/screens/Symbols.h" #include "components/settings/Settings.h" #include "displayapp/InfiniTimeTheme.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { constexpr int16_t POS_Y_TEXT = -7; @@ -30,7 +32,7 @@ SettingSetTime::SettingSetTime(Pinetime::Controllers::DateTime& dateTimeControll : dateTimeController {dateTimeController}, settingsController {settingsController}, settingSetDateTime {settingSetDateTime} { lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(title, "Set current time"); + lv_label_set_text_static(title, Translate(settingsController.GetLanguage(), StringId::SetCurrentTime)); lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 15, 15); @@ -68,7 +70,7 @@ SettingSetTime::SettingSetTime(Pinetime::Controllers::DateTime& dateTimeControll lv_obj_set_size(btnSetTime, 120, 50); lv_obj_align(btnSetTime, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0); lblSetTime = lv_label_create(btnSetTime, nullptr); - lv_label_set_text_static(lblSetTime, "Set"); + lv_label_set_text_static(lblSetTime, Translate(settingsController.GetLanguage(), StringId::Set)); lv_obj_set_style_local_bg_color(btnSetTime, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, Colors::bgAlt); lv_obj_set_style_local_text_color(lblSetTime, LV_LABEL_PART_MAIN, LV_STATE_DISABLED, LV_COLOR_GRAY); lv_obj_set_event_cb(btnSetTime, SetTimeEventHandler); diff --git a/src/displayapp/screens/settings/SettingShakeThreshold.cpp b/src/displayapp/screens/settings/SettingShakeThreshold.cpp index be67cc9563..ecee8d2432 100644 --- a/src/displayapp/screens/settings/SettingShakeThreshold.cpp +++ b/src/displayapp/screens/settings/SettingShakeThreshold.cpp @@ -4,8 +4,10 @@ #include "displayapp/screens/Screen.h" #include "displayapp/screens/Symbols.h" #include "displayapp/InfiniTimeTheme.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { void event_handler(lv_obj_t* obj, lv_event_t event) { @@ -20,7 +22,7 @@ SettingShakeThreshold::SettingShakeThreshold(Controllers::Settings& settingsCont : settingsController {settingsController}, motionController {motionController}, systemTask {systemTask} { lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(title, "Wake Sensitivity"); + lv_label_set_text_static(title, Translate(settingsController.GetLanguage(), StringId::WakeSensitivity)); lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 0, 0); @@ -57,7 +59,7 @@ SettingShakeThreshold::SettingShakeThreshold(Controllers::Settings& settingsCont lv_obj_align(calButton, lv_scr_act(), LV_ALIGN_IN_BOTTOM_MID, 0, 0); lv_btn_set_checkable(calButton, true); calLabel = lv_label_create(calButton, nullptr); - lv_label_set_text_static(calLabel, "Calibrate"); + lv_label_set_text_static(calLabel, Translate(settingsController.GetLanguage(), StringId::Calibrate)); lv_arc_set_value(positionArc, settingsController.GetShakeThreshold()); @@ -91,7 +93,7 @@ void SettingShakeThreshold::Refresh() { calibrating = 2; lv_obj_set_style_local_bg_color(calButton, LV_BTN_PART_MAIN, LV_STATE_CHECKED, LV_COLOR_RED); lv_obj_set_style_local_bg_color(calButton, LV_BTN_PART_MAIN, LV_STATE_CHECKED, LV_COLOR_RED); - lv_label_set_text_static(calLabel, "Shake!"); + lv_label_set_text_static(calLabel, Translate(settingsController.GetLanguage(), StringId::Shake)); } } if (calibrating == 2) { @@ -121,13 +123,13 @@ void SettingShakeThreshold::UpdateSelected(lv_obj_t* object, lv_event_t event) { lv_arc_set_value(positionArc, 0); calibrating = 1; vCalTime = xTaskGetTickCount(); - lv_label_set_text_static(calLabel, "Ready!"); + lv_label_set_text_static(calLabel, Translate(settingsController.GetLanguage(), StringId::Ready)); lv_obj_set_click(positionArc, false); lv_obj_set_style_local_bg_color(calButton, LV_BTN_PART_MAIN, LV_STATE_CHECKED, Colors::highlight); } else if (lv_btn_get_state(calButton) == LV_BTN_STATE_RELEASED) { calibrating = 0; lv_obj_set_click(positionArc, true); - lv_label_set_text_static(calLabel, "Calibrate"); + lv_label_set_text_static(calLabel, Translate(settingsController.GetLanguage(), StringId::Calibrate)); } break; } diff --git a/src/displayapp/screens/settings/SettingSteps.cpp b/src/displayapp/screens/settings/SettingSteps.cpp index b8d5c4055a..de0a536ad0 100644 --- a/src/displayapp/screens/settings/SettingSteps.cpp +++ b/src/displayapp/screens/settings/SettingSteps.cpp @@ -3,8 +3,10 @@ #include "displayapp/DisplayApp.h" #include "displayapp/screens/Symbols.h" #include "displayapp/InfiniTimeTheme.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { void event_handler(lv_obj_t* obj, lv_event_t event) { @@ -27,7 +29,7 @@ SettingSteps::SettingSteps(Pinetime::Controllers::Settings& settingsController) lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT); lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(title, "Daily steps goal"); + lv_label_set_text_static(title, Translate(settingsController.GetLanguage(), StringId::DailyStepsGoal)); lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 15, 15); diff --git a/src/displayapp/screens/settings/SettingTimeFormat.cpp b/src/displayapp/screens/settings/SettingTimeFormat.cpp index df46d8a829..acabf70e53 100644 --- a/src/displayapp/screens/settings/SettingTimeFormat.cpp +++ b/src/displayapp/screens/settings/SettingTimeFormat.cpp @@ -4,8 +4,10 @@ #include "displayapp/screens/Styles.h" #include "displayapp/screens/Screen.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { struct Option { @@ -46,7 +48,7 @@ SettingTimeFormat::SettingTimeFormat(Pinetime::Controllers::Settings& settingsCo : checkboxList( 0, 1, - "Time format", + Translate(settingsController.GetLanguage(), StringId::TimeFormat), Symbols::clock, GetDefaultOption(settingsController.GetClockType()), [&settings = settingsController](uint32_t index) { diff --git a/src/displayapp/screens/settings/SettingWakeUp.cpp b/src/displayapp/screens/settings/SettingWakeUp.cpp index 3413257dc3..fd7c0df02b 100644 --- a/src/displayapp/screens/settings/SettingWakeUp.cpp +++ b/src/displayapp/screens/settings/SettingWakeUp.cpp @@ -4,9 +4,11 @@ #include "displayapp/screens/Screen.h" #include "displayapp/screens/Symbols.h" #include "components/settings/Settings.h" +#include "displayapp/localization/Localization.h" #include "displayapp/screens/Styles.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { void event_handler(lv_obj_t* obj, lv_event_t event) { @@ -31,7 +33,7 @@ SettingWakeUp::SettingWakeUp(Pinetime::Controllers::Settings& settingsController lv_cont_set_layout(container1, LV_LAYOUT_COLUMN_LEFT); lv_obj_t* title = lv_label_create(lv_scr_act(), nullptr); - lv_label_set_text_static(title, "Wake Up"); + lv_label_set_text_static(title, Translate(settingsController.GetLanguage(), StringId::WakeUp)); lv_label_set_align(title, LV_LABEL_ALIGN_CENTER); lv_obj_align(title, lv_scr_act(), LV_ALIGN_IN_TOP_MID, 15, 15); @@ -43,7 +45,7 @@ SettingWakeUp::SettingWakeUp(Pinetime::Controllers::Settings& settingsController for (unsigned int i = 0; i < options.size(); i++) { cbOption[i] = lv_checkbox_create(container1, nullptr); - lv_checkbox_set_text_static(cbOption[i], options[i].name); + lv_checkbox_set_text_static(cbOption[i], Translate(settingsController.GetLanguage(), options[i].name)); if (settingsController.isWakeUpModeOn(static_cast(i))) { lv_checkbox_set_checked(cbOption[i], true); } diff --git a/src/displayapp/screens/settings/SettingWakeUp.h b/src/displayapp/screens/settings/SettingWakeUp.h index 61edabce34..b9c3c3988f 100644 --- a/src/displayapp/screens/settings/SettingWakeUp.h +++ b/src/displayapp/screens/settings/SettingWakeUp.h @@ -4,6 +4,7 @@ #include #include #include "components/settings/Settings.h" +#include "displayapp/localization/Localization.h" #include "displayapp/screens/Screen.h" namespace Pinetime { @@ -21,16 +22,16 @@ namespace Pinetime { private: struct Option { Controllers::Settings::WakeUpMode wakeUpMode; - const char* name; + Localization::StringId name; }; Controllers::Settings& settingsController; static constexpr std::array options = {{ - {Controllers::Settings::WakeUpMode::SingleTap, "Single Tap"}, - {Controllers::Settings::WakeUpMode::DoubleTap, "Double Tap"}, - {Controllers::Settings::WakeUpMode::RaiseWrist, "Raise Wrist"}, - {Controllers::Settings::WakeUpMode::Shake, "Shake Wake"}, - {Controllers::Settings::WakeUpMode::LowerWrist, "Lower Wrist"}, + {Controllers::Settings::WakeUpMode::SingleTap, Localization::StringId::SingleTap}, + {Controllers::Settings::WakeUpMode::DoubleTap, Localization::StringId::DoubleTap}, + {Controllers::Settings::WakeUpMode::RaiseWrist, Localization::StringId::RaiseWrist}, + {Controllers::Settings::WakeUpMode::Shake, Localization::StringId::ShakeWake}, + {Controllers::Settings::WakeUpMode::LowerWrist, Localization::StringId::LowerWrist}, }}; lv_obj_t* cbOption[options.size()]; diff --git a/src/displayapp/screens/settings/SettingWatchFace.cpp b/src/displayapp/screens/settings/SettingWatchFace.cpp index 0d5168d26f..607b506d93 100644 --- a/src/displayapp/screens/settings/SettingWatchFace.cpp +++ b/src/displayapp/screens/settings/SettingWatchFace.cpp @@ -3,10 +3,10 @@ #include "displayapp/DisplayApp.h" #include "displayapp/screens/Screen.h" #include "components/settings/Settings.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; - -constexpr const char* SettingWatchFace::title; +using namespace Pinetime::Applications::Localization; constexpr const char* SettingWatchFace::symbol; namespace { @@ -82,7 +82,7 @@ std::unique_ptr SettingWatchFace::CreateScreen(unsigned int screenNum) c return std::make_unique( screenNum, nScreens, - title, + Translate(settingsController.GetLanguage(), StringId::WatchFace), symbol, static_cast(IndexOf(watchfaceItems, settingsController.GetWatchFace())), [this, &settings = settingsController](uint32_t index) { diff --git a/src/displayapp/screens/settings/SettingWatchFace.h b/src/displayapp/screens/settings/SettingWatchFace.h index 9edc1f7ac8..cd88fbe40f 100644 --- a/src/displayapp/screens/settings/SettingWatchFace.h +++ b/src/displayapp/screens/settings/SettingWatchFace.h @@ -37,14 +37,13 @@ namespace Pinetime { auto CreateScreenList() const; std::unique_ptr CreateScreen(unsigned int screenNum) const; - static constexpr int settingsPerScreen = 4; + static constexpr int settingsPerScreen = CheckboxList::MaxItems; std::array watchfaceItems; - static constexpr int nScreens = UserWatchFaceTypes::Count > 0 ? (UserWatchFaceTypes ::Count - 1) / settingsPerScreen + 1 : 1; + static constexpr int nScreens = UserWatchFaceTypes::Count > 0 ? (UserWatchFaceTypes::Count - 1) / settingsPerScreen + 1 : 1; Controllers::Settings& settingsController; Pinetime::Controllers::FS& filesystem; - static constexpr const char* title = "Watch face"; static constexpr const char* symbol = Symbols::home; ScreenList screens; diff --git a/src/displayapp/screens/settings/SettingWeatherFormat.cpp b/src/displayapp/screens/settings/SettingWeatherFormat.cpp index 22d281b2ad..1c768defec 100644 --- a/src/displayapp/screens/settings/SettingWeatherFormat.cpp +++ b/src/displayapp/screens/settings/SettingWeatherFormat.cpp @@ -6,28 +6,30 @@ #include "displayapp/screens/Styles.h" #include "displayapp/screens/Screen.h" #include "displayapp/screens/Symbols.h" +#include "displayapp/localization/Localization.h" using namespace Pinetime::Applications::Screens; +using namespace Pinetime::Applications::Localization; namespace { struct Option { Pinetime::Controllers::Settings::WeatherFormat weatherFormat; - const char* name; + StringId name; }; constexpr std::array options = {{ - {Pinetime::Controllers::Settings::WeatherFormat::Metric, "Metric"}, - {Pinetime::Controllers::Settings::WeatherFormat::Imperial, "Imperial"}, + {Pinetime::Controllers::Settings::WeatherFormat::Metric, StringId::Metric}, + {Pinetime::Controllers::Settings::WeatherFormat::Imperial, StringId::Imperial}, }}; - std::array CreateOptionArray() { + std::array CreateOptionArray(Language language) { std::array optionArray; for (size_t i = 0; i < CheckboxList::MaxItems; i++) { if (i >= options.size()) { optionArray[i].name = ""; optionArray[i].enabled = false; } else { - optionArray[i].name = options[i].name; + optionArray[i].name = Translate(language, options[i].name); optionArray[i].enabled = true; } } @@ -48,14 +50,14 @@ SettingWeatherFormat::SettingWeatherFormat(Pinetime::Controllers::Settings& sett : checkboxList( 0, 1, - "Weather format", + Translate(settingsController.GetLanguage(), StringId::Weather), Symbols::cloudSunRain, GetDefaultOption(settingsController.GetWeatherFormat()), [&settings = settingsController](uint32_t index) { settings.SetWeatherFormat(options[index].weatherFormat); settings.SaveSettings(); }, - CreateOptionArray()) { + CreateOptionArray(settingsController.GetLanguage())) { } SettingWeatherFormat::~SettingWeatherFormat() { diff --git a/src/displayapp/screens/settings/Settings.h b/src/displayapp/screens/settings/Settings.h index 32ac3ca943..ee2503dfbe 100644 --- a/src/displayapp/screens/settings/Settings.h +++ b/src/displayapp/screens/settings/Settings.h @@ -32,24 +32,25 @@ namespace Pinetime { static constexpr int nScreens = 4; static constexpr std::array entries {{ - {Symbols::sun, "Display", Apps::SettingDisplay}, - {Symbols::eye, "Wake Up", Apps::SettingWakeUp}, - {Symbols::clock, "Time format", Apps::SettingTimeFormat}, - {Symbols::home, "Watch face", Apps::SettingWatchFace}, - - {Symbols::shoe, "Steps", Apps::SettingSteps}, - {Symbols::heartBeat, "Heartrate", Apps::SettingHeartRate}, - {Symbols::clock, "Date & Time", Apps::SettingSetDateTime}, - {Symbols::cloudSunRain, "Weather", Apps::SettingWeatherFormat}, - - {Symbols::batteryHalf, "Battery", Apps::BatteryInfo}, - {Symbols::clock, "Chimes", Apps::SettingChimes}, - {Symbols::tachometer, "Shake Calib.", Apps::SettingShakeThreshold}, - {Symbols::check, "Firmware", Apps::FirmwareValidation}, - - {Symbols::shieldAlt, "Over-the-air", Apps::SettingOTA}, - {Symbols::bluetooth, "Bluetooth", Apps::SettingBluetooth}, - {Symbols::list, "About", Apps::SysInfo}, + {Symbols::sun, Localization::StringId::Display, Apps::SettingDisplay}, + {Symbols::eye, Localization::StringId::WakeUp, Apps::SettingWakeUp}, + {Symbols::clock, Localization::StringId::TimeFormat, Apps::SettingTimeFormat}, + {Symbols::home, Localization::StringId::WatchFace, Apps::SettingWatchFace}, + + {Symbols::shoe, Localization::StringId::Steps, Apps::SettingSteps}, + {Symbols::heartBeat, Localization::StringId::HeartRate, Apps::SettingHeartRate}, + {Symbols::clock, Localization::StringId::DateTime, Apps::SettingSetDateTime}, + {Symbols::cloudSunRain, Localization::StringId::Weather, Apps::SettingWeatherFormat}, + + {Symbols::batteryHalf, Localization::StringId::Battery, Apps::BatteryInfo}, + {Symbols::clock, Localization::StringId::Chimes, Apps::SettingChimes}, + {Symbols::tachometer, Localization::StringId::ShakeCalibration, Apps::SettingShakeThreshold}, + {Symbols::check, Localization::StringId::Firmware, Apps::FirmwareValidation}, + + {Symbols::shieldAlt, Localization::StringId::Ota, Apps::SettingOTA}, + {Symbols::bluetooth, Localization::StringId::Bluetooth, Apps::SettingBluetooth}, + {Symbols::map, Localization::StringId::Language, Apps::SettingLanguage}, + {Symbols::list, Localization::StringId::About, Apps::SysInfo}, }}; ScreenList screens; }; diff --git a/src/displayapp/widgets/Counter.cpp b/src/displayapp/widgets/Counter.cpp index b486e3727f..fed07373ee 100644 --- a/src/displayapp/widgets/Counter.cpp +++ b/src/displayapp/widgets/Counter.cpp @@ -87,7 +87,7 @@ void Counter::UpdateLabel() { lv_label_set_text_fmt(number, "%.*i", leadingZeroCount, value - 12); } } else if (monthMode) { - lv_label_set_text(number, Controllers::DateTime::MonthShortToStringLow(static_cast(value))); + lv_label_set_text(number, Controllers::DateTime::MonthShortToStringLow(static_cast(value), monthLanguage)); } else { lv_label_set_text_fmt(number, "%.*i", leadingZeroCount, value); } @@ -101,8 +101,9 @@ void Counter::EnableTwelveHourMode() { // Value is kept between 1 and 12, but the displayed value is the corresponding month // Make sure to set the max and min values to 1 and 12. Otherwise behaviour is undefined -void Counter::EnableMonthMode() { +void Counter::EnableMonthMode(Pinetime::Controllers::Settings::Language language) { monthMode = true; + monthLanguage = language; } // Counter cannot be resized after creation, diff --git a/src/displayapp/widgets/Counter.h b/src/displayapp/widgets/Counter.h index 825860b8d9..47a79b6309 100644 --- a/src/displayapp/widgets/Counter.h +++ b/src/displayapp/widgets/Counter.h @@ -1,5 +1,6 @@ #pragma once #include +#include "components/settings/Settings.h" namespace Pinetime { namespace Applications { @@ -15,7 +16,7 @@ namespace Pinetime { void HideControls(); void ShowControls(); void EnableTwelveHourMode(); - void EnableMonthMode(); + void EnableMonthMode(Pinetime::Controllers::Settings::Language language = Pinetime::Controllers::Settings::Language::English); void SetMax(int newMax); void SetValueChangedEventCallback(void* userData, void (*handler)(void* userData)); @@ -44,6 +45,7 @@ namespace Pinetime { const int leadingZeroCount; bool twelveHourMode = false; bool monthMode = false; + Pinetime::Controllers::Settings::Language monthLanguage = Pinetime::Controllers::Settings::Language::English; lv_font_t& font; void* userData = nullptr;