diff --git a/platformio.ini b/platformio.ini index 7d5f9b24c..e8d2b2227 100644 --- a/platformio.ini +++ b/platformio.ini @@ -62,6 +62,7 @@ build_flags = -Wno-missing-field-initializers -D MAX_THREADS=40 ; As we've split modules, we have more threads to manage #-DBUILD_EPOCH=$UNIX_TIME ; set in platformio-custom.py now #-D OLED_PL=1 + #-D OLED_ZH=1 ; uncomment for compile-time Chinese font (runtime switching works without this on TFT) #-D DEBUG_HEAP=1 ; uncomment to add free heap space / memory leak debugging logs #-D DEBUG_LOOP_TIMING=1 ; uncomment to add main loop timing logs diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index bc65a08c8..29bfbec44 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -39,6 +39,7 @@ along with this program. If not, see . #include "draw/NodeListRenderer.h" #include "draw/NotificationRenderer.h" #include "draw/UIRenderer.h" +#include "graphics/fonts/OLEDDisplayFontsZH.h" #include "graphics/TFTColorRegions.h" #include "modules/CannedMessageModule.h" #include "security/LockdownDisplay.h" @@ -60,6 +61,7 @@ along with this program. If not, see . #include "graphics/TFTPalette.h" #include "graphics/emotes.h" #include "graphics/images.h" +#include "graphics/l10n/Strings.h" #include "input/TouchScreenImpl1.h" #include "main.h" #include "mesh-pb-constants.h" @@ -731,7 +733,10 @@ void Screen::setup() // Enable display rendering useDisplay = true; - // Load saved brightness from UI config + // Set the runtime display language from persisted config + meshtastic::l10n::currentDisplayLanguage = uiconfig.language; + + // Load brightness from UI config // For OLED displays (SSD1306), default brightness is 255 if not set if (uiconfig.screen_brightness == 0) { #if defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) @@ -819,6 +824,9 @@ void Screen::setup() // Enable UTF-8 to display mapping dispdev->setFontTableLookupFunction(customFontTableLookup); + // Load CJK (Chinese) font if language requires it + loadCJKFontIfNeeded(); + #ifdef USERPREFS_OEM_TEXT logo_timeout *= 2; // Give more time for branded boot logos #endif @@ -985,6 +993,13 @@ int32_t Screen::runOnce() return RUN_SAME; } + // Detect language changes from remote config (phone app) + if (uiconfig.language != meshtastic::l10n::currentDisplayLanguage) { + meshtastic::l10n::currentDisplayLanguage = uiconfig.language; + loadCJKFontIfNeeded(); + setFrames(FOCUS_PRESERVE); + } + if (displayHeight == 0) { displayHeight = dispdev->getHeight(); } @@ -2210,6 +2225,272 @@ bool Screen::isOverlayBannerShowing() return NotificationRenderer::isOverlayBannerShowing(); } +char Screen::customFontTableLookup(const uint8_t ch) +{ + static uint8_t LASTCHAR; + static bool SKIPREST; + static int8_t cjkRemaining = 0; + + if (cjkRemaining > 0) { + cjkRemaining--; + LASTCHAR = ch; + return (uint8_t)0; + } + + if (ch < 128) { + LASTCHAR = 0; + SKIPREST = false; + cjkRemaining = 0; + return ch; + } + + uint8_t last = LASTCHAR; + LASTCHAR = ch; + + switch (last) { + case 0xC2: { + SKIPREST = false; + return (uint8_t)ch; + } + case 0xC3: { + SKIPREST = false; + return (uint8_t)(ch | 0xC0); + } + } + + if (ch == 0xC2 || ch == 0xC3) + return (uint8_t)0; + +#if defined(OLED_PL) + switch (last) { + case 0xC3: { + if (ch == 147) + return (uint8_t)(ch); + else if (ch == 179) + return (uint8_t)(148); + else + return (uint8_t)(ch | 0xC0); + break; + } + case 0xC4: { + SKIPREST = false; + return (uint8_t)(ch); + } + case 0xC5: { + SKIPREST = false; + if (ch == 132) + return (uint8_t)(136); + else if (ch == 186) + return (uint8_t)(137); + else + return (uint8_t)(ch); + break; + } + } + if (ch == 0xC2 || ch == 0xC3 || ch == 0xC4 || ch == 0xC5) + return (uint8_t)0; +#endif + +#if defined(OLED_UA) || defined(OLED_RU) + switch (last) { + case 0xC3: { + SKIPREST = false; + return (uint8_t)(ch | 0xC0); + } + case 0xD0: { + SKIPREST = false; + if (ch == 132) + return (uint8_t)(170); + if (ch == 134) + return (uint8_t)(178); + if (ch == 135) + return (uint8_t)(175); + if (ch == 129) + return (uint8_t)(168); + if (ch > 143 && ch < 192) + return (uint8_t)(ch + 48); + break; + } + case 0xD1: { + SKIPREST = false; + if (ch == 148) + return (uint8_t)(186); + if (ch == 150) + return (uint8_t)(179); + if (ch == 151) + return (uint8_t)(191); + if (ch == 145) + return (uint8_t)(184); + if (ch > 127 && ch < 144) + return (uint8_t)(ch + 112); + break; + } + case 0xD2: { + SKIPREST = false; + if (ch == 144) + return (uint8_t)(165); + if (ch == 145) + return (uint8_t)(180); + break; + } + } + if (ch == 0xC2 || ch == 0xC3 || ch == 0x82 || ch == 0xD0 || ch == 0xD1) + return (uint8_t)0; +#endif + +#if defined(OLED_CS) + switch (last) { + case 0xC2: { + SKIPREST = false; + return (uint8_t)ch; + } + case 0xC3: { + SKIPREST = false; + return (uint8_t)(ch | 0xC0); + } + case 0xC4: { + SKIPREST = false; + if (ch == 140) + return (uint8_t)(129); + if (ch == 141) + return (uint8_t)(138); + if (ch == 142) + return (uint8_t)(130); + if (ch == 143) + return (uint8_t)(139); + if (ch == 154) + return (uint8_t)(131); + if (ch == 155) + return (uint8_t)(140); + if (ch == 185) + return (uint8_t)(147); + if (ch == 186) + return (uint8_t)(148); + if (ch == 189) + return (uint8_t)(149); + if (ch == 190) + return (uint8_t)(150); + break; + } + case 0xC5: { + SKIPREST = false; + if (ch == 135) + return (uint8_t)(132); + if (ch == 136) + return (uint8_t)(141); + if (ch == 152) + return (uint8_t)(133); + if (ch == 153) + return (uint8_t)(142); + if (ch == 160) + return (uint8_t)(134); + if (ch == 161) + return (uint8_t)(143); + if (ch == 164) + return (uint8_t)(135); + if (ch == 165) + return (uint8_t)(144); + if (ch == 174) + return (uint8_t)(136); + if (ch == 175) + return (uint8_t)(145); + if (ch == 189) + return (uint8_t)(137); + if (ch == 190) + return (uint8_t)(146); + if (ch == 148) + return (uint8_t)(151); + if (ch == 149) + return (uint8_t)(152); + break; + } + } + if (ch == 0xC2 || ch == 0xC3 || ch == 0xC4 || ch == 0xC5) + return (uint8_t)0; +#endif + +#if defined(OLED_GR) + switch (last) { + case 0xC3: { + SKIPREST = false; + return (uint8_t)(ch | 0xC0); + } + case 0xCE: { + SKIPREST = false; + if (ch >= 145 && ch <= 161) + return (uint8_t)(ch + 48); + else if (ch >= 163 && ch <= 169) + return (uint8_t)(ch + 48); + else if (ch >= 177 && ch <= 193) + return (uint8_t)(ch + 48); + break; + } + case 0xCF: { + SKIPREST = false; + if (ch >= 130 && ch <= 137) + return (uint8_t)(ch + 112); + break; + } + } + if (ch == 0xC2 || ch == 0xC3 || ch == 0xCE || ch == 0xCF) + return (uint8_t)0; +#endif + + if (ch >= 0xE2 && ch <= 0xEF) { + cjkRemaining = 2; + return (uint8_t)0; + } + + if (SKIPREST) + return (uint8_t)0; + SKIPREST = true; + + return (uint8_t)191; +} + +void Screen::drawTextL10n(int16_t x, int16_t y, const char *text, OLEDDISPLAY_TEXT_ALIGNMENT alignment) +{ + if (!dispdev || !text) + return; + +#if defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || \ + defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(RAK14014) + if (meshtastic::l10n::currentDisplayLanguage == meshtastic_Language_SIMPLIFIED_CHINESE || + meshtastic::l10n::currentDisplayLanguage == meshtastic_Language_TRADITIONAL_CHINESE) { + TFTDisplay *tftDisp = static_cast(dispdev); + if (tftDisp->hasCJKFont()) { + tftDisp->drawStringCJK(x, y, text); + return; + } + } +#endif + + dispdev->setFont(FONT_SMALL); + dispdev->setTextAlignment(alignment); + dispdev->drawString(x, y, text); +} + +void Screen::setFontL10n(const uint8_t *fontData) +{ + if (!dispdev) + return; + dispdev->setFont(fontData); +} + +void Screen::loadCJKFontIfNeeded() +{ +#if defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || \ + defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(RAK14014) + if (meshtastic::l10n::currentDisplayLanguage == meshtastic_Language_SIMPLIFIED_CHINESE || + meshtastic::l10n::currentDisplayLanguage == meshtastic_Language_TRADITIONAL_CHINESE) { + TFTDisplay *tftDisp = static_cast(dispdev); + if (!tftDisp->hasCJKFont()) { + tftDisp->loadCJKFont(FontZH_CN_16, FontZH_CN_16_size); + } + } +#endif +} + } // namespace graphics #else diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index 37acfbc96..9df71fd94 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -366,255 +366,13 @@ class Screen : public concurrency::OSThread enqueueCmd(ScreenCmd{.cmd = Cmd::NOOP}); } - /// Overrides the default utf8 character conversion, to replace empty space with question marks - static char customFontTableLookup(const uint8_t ch) - { - // UTF-8 to font table index converter - // Code from http://playground.arduino.cc/Main/Utf8ascii - static uint8_t LASTCHAR; - static bool SKIPREST; // Only display a single unconvertable-character symbol per sequence of unconvertable characters + static char customFontTableLookup(const uint8_t ch); - if (ch < 128) { // Standard ASCII-set 0..0x7F handling - LASTCHAR = 0; - SKIPREST = false; - return ch; - } + void drawTextL10n(int16_t x, int16_t y, const char *text, OLEDDISPLAY_TEXT_ALIGNMENT alignment = TEXT_ALIGN_LEFT); + void setFontL10n(const uint8_t *fontData); - uint8_t last = LASTCHAR; // get last char - LASTCHAR = ch; - - switch (last) { - case 0xC2: { - SKIPREST = false; - return (uint8_t)ch; - } - - case 0xC3: { - SKIPREST = false; - return (uint8_t)(ch | 0xC0); - } - } - - // We want to strip out prefix chars for two-byte char formats - if (ch == 0xC2 || ch == 0xC3) - return (uint8_t)0; - -#if defined(OLED_PL) - - switch (last) { - case 0xC3: { - - if (ch == 147) - return (uint8_t)(ch); // Ó - else if (ch == 179) - return (uint8_t)(148); // ó - else - return (uint8_t)(ch | 0xC0); - break; - } - - case 0xC4: { - SKIPREST = false; - return (uint8_t)(ch); - } - - case 0xC5: { - SKIPREST = false; - if (ch == 132) - return (uint8_t)(136); // ń - else if (ch == 186) - return (uint8_t)(137); // ź - else - return (uint8_t)(ch); - break; - } - } - - // We want to strip out prefix chars for two-byte char formats - if (ch == 0xC2 || ch == 0xC3 || ch == 0xC4 || ch == 0xC5) - return (uint8_t)0; - -#endif - -#if defined(OLED_UA) || defined(OLED_RU) - - switch (last) { - case 0xC3: { - SKIPREST = false; - return (uint8_t)(ch | 0xC0); - } - // map UTF-8 cyrillic chars to it Windows-1251 (CP-1251) ASCII codes - // note: in this case we must use compatible font - provided ArialMT_Plain_10/16/24 by 'ThingPulse/esp8266-oled-ssd1306' - // library have empty chars for non-latin ASCII symbols - case 0xD0: { - SKIPREST = false; - if (ch == 132) - return (uint8_t)(170); // Є - if (ch == 134) - return (uint8_t)(178); // І - if (ch == 135) - return (uint8_t)(175); // Ї - if (ch == 129) - return (uint8_t)(168); // Ё - if (ch > 143 && ch < 192) - return (uint8_t)(ch + 48); - break; - } - case 0xD1: { - SKIPREST = false; - if (ch == 148) - return (uint8_t)(186); // є - if (ch == 150) - return (uint8_t)(179); // і - if (ch == 151) - return (uint8_t)(191); // ї - if (ch == 145) - return (uint8_t)(184); // ё - if (ch > 127 && ch < 144) - return (uint8_t)(ch + 112); - break; - } - case 0xD2: { - SKIPREST = false; - if (ch == 144) - return (uint8_t)(165); // Ґ - if (ch == 145) - return (uint8_t)(180); // ґ - break; - } - } - - // We want to strip out prefix chars for two-byte char formats - if (ch == 0xC2 || ch == 0xC3 || ch == 0x82 || ch == 0xD0 || ch == 0xD1) - return (uint8_t)0; - -#endif - -#if defined(OLED_CS) - - switch (last) { - case 0xC2: { - SKIPREST = false; - return (uint8_t)ch; - } - - case 0xC3: { - SKIPREST = false; - return (uint8_t)(ch | 0xC0); - } - - case 0xC4: { - SKIPREST = false; - if (ch == 140) - return (uint8_t)(129); // Č - if (ch == 141) - return (uint8_t)(138); // č - if (ch == 142) - return (uint8_t)(130); // Ď - if (ch == 143) - return (uint8_t)(139); // ď - if (ch == 154) - return (uint8_t)(131); // Ě - if (ch == 155) - return (uint8_t)(140); // ě - // Slovak specific glyphs - if (ch == 185) - return (uint8_t)(147); // Ĺ - if (ch == 186) - return (uint8_t)(148); // ĺ - if (ch == 189) - return (uint8_t)(149); // Ľ - if (ch == 190) - return (uint8_t)(150); // ľ - break; - } - - case 0xC5: { - SKIPREST = false; - if (ch == 135) - return (uint8_t)(132); // Ň - if (ch == 136) - return (uint8_t)(141); // ň - if (ch == 152) - return (uint8_t)(133); // Ř - if (ch == 153) - return (uint8_t)(142); // ř - if (ch == 160) - return (uint8_t)(134); // Š - if (ch == 161) - return (uint8_t)(143); // š - if (ch == 164) - return (uint8_t)(135); // Ť - if (ch == 165) - return (uint8_t)(144); // ť - if (ch == 174) - return (uint8_t)(136); // Ů - if (ch == 175) - return (uint8_t)(145); // ů - if (ch == 189) - return (uint8_t)(137); // Ž - if (ch == 190) - return (uint8_t)(146); // ž - // Slovak specific glyphs - if (ch == 148) - return (uint8_t)(151); // Ŕ - if (ch == 149) - return (uint8_t)(152); // ŕ - break; - } - } - - // We want to strip out prefix chars for two-byte char formats - if (ch == 0xC2 || ch == 0xC3 || ch == 0xC4 || ch == 0xC5) - return (uint8_t)0; - -#endif - -#if defined(OLED_GR) - - switch (last) { - case 0xC3: { - SKIPREST = false; - return (uint8_t)(ch | 0xC0); - } - // Map UTF-8 Greek chars to Windows-1253 (CP-1253) ASCII codes - case 0xCE: { - SKIPREST = false; - // Uppercase Greek: Α-Ρ (U+0391-U+03A1) -> CP-1253 193-209 - if (ch >= 145 && ch <= 161) - return (uint8_t)(ch + 48); - // Uppercase Greek: Σ-Ω (U+03A3-U+03A9) -> CP-1253 211-217 - else if (ch >= 163 && ch <= 169) - return (uint8_t)(ch + 48); - // Lowercase Greek: α-ρ (U+03B1-U+03C1) -> CP-1253 225-241 - else if (ch >= 177 && ch <= 193) - return (uint8_t)(ch + 48); - break; - } - case 0xCF: { - SKIPREST = false; - // Lowercase Greek: ς-ω (U+03C2-U+03C9) -> CP-1253 242-249 - if (ch >= 130 && ch <= 137) - return (uint8_t)(ch + 112); - break; - } - } - - // We want to strip out prefix chars for two-byte Greek char formats - if (ch == 0xC2 || ch == 0xC3 || ch == 0xCE || ch == 0xCF) - return (uint8_t)0; - -#endif - - // If we already returned an unconvertable-character symbol for this unconvertable-character sequence, return NULs for the - // rest of it - if (SKIPREST) - return (uint8_t)0; - SKIPREST = true; - - return (uint8_t)191; // otherwise: return ¿ if character can't be converted (note that the font map we're using doesn't - // stick to standard EASCII codes) - } + // Load CJK font if language requires it (called from setup & on language change) + void loadCJKFontIfNeeded(); /// Returns a handle to the DebugInfo screen. // diff --git a/src/graphics/ScreenFonts.h b/src/graphics/ScreenFonts.h index 624465f33..1769e3fb7 100644 --- a/src/graphics/ScreenFonts.h +++ b/src/graphics/ScreenFonts.h @@ -20,6 +20,10 @@ #include "graphics/fonts/OLEDDisplayFontsGR.h" #endif +#ifdef OLED_ZH +#include "graphics/fonts/OLEDDisplayFontsZH.h" +#endif + #if (defined(CROWPANEL_ESP32S3_5_EPAPER) || defined(T5_S3_EPAPER_PRO)) && defined(USE_EINK) #include "graphics/fonts/EinkDisplayFonts.h" #endif diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp index 4ef707efe..b1ce78e46 100644 --- a/src/graphics/TFTDisplay.cpp +++ b/src/graphics/TFTDisplay.cpp @@ -1636,4 +1636,35 @@ bool TFTDisplay::connect() return true; } +bool TFTDisplay::loadCJKFont(const uint8_t *vlwData, uint32_t vlwDataLen) +{ + if (!tft || !vlwData || vlwDataLen == 0) + return false; + cjkFontLoaded = tft->loadFont(vlwData); + if (cjkFontLoaded) { + LOG_INFO("CJK font loaded (%u bytes)", vlwDataLen); + } else { + LOG_WARN("Failed to load CJK font"); + } + return cjkFontLoaded; +} + +void TFTDisplay::drawStringCJK(int16_t x, int16_t y, const char *text) +{ + if (!tft || !text || !cjkFontLoaded) + return; + concurrency::LockGuard g(spiLock); + uint16_t fgColor = getThemeDefaultOnColor(); + tft->setTextColor(fgColor, getThemeDefaultOffColor()); + tft->setCursor(x, y); + tft->print(text); +} + +int16_t TFTDisplay::getStringWidthCJK(const char *text) +{ + if (!tft || !text || !cjkFontLoaded) + return 0; + return tft->textWidth(text); +} + #endif // USE_TFTDISPLAY diff --git a/src/graphics/TFTDisplay.h b/src/graphics/TFTDisplay.h index 2c86f05d2..ea955383e 100644 --- a/src/graphics/TFTDisplay.h +++ b/src/graphics/TFTDisplay.h @@ -38,6 +38,12 @@ class TFTDisplay : public OLEDDisplay // Functions for changing display brightness void setDisplayBrightness(uint8_t); + // CJK (Chinese/Japanese/Korean) text rendering via LovyanGFX + bool loadCJKFont(const uint8_t *vlwData, uint32_t vlwDataLen); + void drawStringCJK(int16_t x, int16_t y, const char *text); + int16_t getStringWidthCJK(const char *text); + bool hasCJKFont() const { return cjkFontLoaded; } + /** * shim to make the abstraction happy * @@ -64,4 +70,5 @@ class TFTDisplay : public OLEDDisplay uint16_t *linePixelBuffer = nullptr; uint16_t *repaintChunkBuffer = nullptr; + bool cjkFontLoaded = false; }; diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 0f4d8d713..e13287f3f 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -13,6 +13,7 @@ #include "graphics/Screen.h" #include "graphics/SharedUIDisplay.h" #include "graphics/TFTColorRegions.h" +#include "graphics/l10n/Strings.h" #include "graphics/draw/MessageRenderer.h" #include "graphics/draw/UIRenderer.h" #include "input/RotaryEncoderInterruptImpl1.h" @@ -2377,9 +2378,9 @@ void menuHandler::screenOptionsMenu() bool hasSupportBrightness = false; #endif - enum optionsNumbers { Back, Brightness, FrameToggles, DisplayUnits, MessageBubbles, Theme }; - static const char *optionsArray[7] = {"Back"}; - static int optionsEnumArray[7] = {Back}; + enum optionsNumbers { Back, Brightness, FrameToggles, DisplayUnits, MessageBubbles, Theme, LangPicker }; + static const char *optionsArray[8] = {"Back"}; + static int optionsEnumArray[8] = {Back}; int options = 1; // Only show brightness for B&W displays @@ -2402,6 +2403,12 @@ void menuHandler::screenOptionsMenu() optionsEnumArray[options++] = Theme; #endif +#if defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || \ + defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(RAK14014) + optionsArray[options] = "Language"; + optionsEnumArray[options++] = LangPicker; +#endif + BannerOverlayOptions bannerOptions; bannerOptions.message = "Display Options"; bannerOptions.optionsArrayPtr = optionsArray; @@ -2423,6 +2430,9 @@ void menuHandler::screenOptionsMenu() } else if (selected == Theme) { menuHandler::menuQueue = menuHandler::ThemeMenu; screen->runNow(); + } else if (selected == LangPicker) { + menuHandler::menuQueue = menuHandler::LanguagePicker; + screen->runNow(); } else { menuQueue = SystemBaseMenu; screen->runNow(); @@ -2903,6 +2913,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) case ThemeMenu: themeMenu(); break; + case LanguagePicker: + languagePickerMenu(); + break; case HamModeConfirm: hamModeConfirmMenu(); break; @@ -2913,6 +2926,44 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) menuQueue = MenuNone; } +void menuHandler::languagePickerMenu() +{ + enum optionsNumbers { Back, English, Chinese }; + static const char *optionsArray[3] = {"Back"}; + static int optionsEnumArray[3] = {Back}; + int options = 1; + + optionsArray[options] = "English"; + optionsEnumArray[options++] = English; + + optionsArray[options] = "Chinese"; + optionsEnumArray[options++] = Chinese; + + BannerOverlayOptions bannerOptions; + bannerOptions.message = "Language"; + bannerOptions.optionsArrayPtr = optionsArray; + bannerOptions.optionsCount = options; + bannerOptions.optionsEnumPtr = optionsEnumArray; + bannerOptions.bannerCallback = [](int selected) -> void { + if (selected == English) { + uiconfig.language = meshtastic_Language_ENGLISH; + meshtastic::l10n::currentDisplayLanguage = meshtastic_Language_ENGLISH; + saveUIConfig(); + } else if (selected == Chinese) { + uiconfig.language = meshtastic_Language_SIMPLIFIED_CHINESE; + meshtastic::l10n::currentDisplayLanguage = meshtastic_Language_SIMPLIFIED_CHINESE; + saveUIConfig(); +#if defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || \ + defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(RAK14014) + screen->loadCJKFontIfNeeded(); +#endif + } + menuQueue = ScreenOptionsMenu; + screen->runNow(); + }; + screen->showOverlayBanner(bannerOptions); +} + void menuHandler::saveUIConfig() { nodeDB->saveProto("/prefs/uiconfig.proto", meshtastic_DeviceUIConfig_size, &meshtastic_DeviceUIConfig_msg, &uiconfig); diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h index b924c9197..9b46160d7 100644 --- a/src/graphics/draw/MenuHandler.h +++ b/src/graphics/draw/MenuHandler.h @@ -56,6 +56,7 @@ class menuHandler DisplayUnits, MessageBubblesMenu, ThemeMenu, + LanguagePicker, HamModeConfirm, LicensedToNormalConfirm }; @@ -107,6 +108,7 @@ class menuHandler static void wifiBaseMenu(); static void wifiToggleMenu(); static void screenOptionsMenu(); + static void languagePickerMenu(); static void powerMenu(); static void nodeNameLengthMenu(); static void frameTogglesMenu(); diff --git a/src/graphics/draw/MessageRenderer.cpp b/src/graphics/draw/MessageRenderer.cpp index a42ef1e08..ec46a8e44 100644 --- a/src/graphics/draw/MessageRenderer.cpp +++ b/src/graphics/draw/MessageRenderer.cpp @@ -13,6 +13,7 @@ #include "graphics/SharedUIDisplay.h" #include "graphics/TFTColorRegions.h" #include "graphics/TFTPalette.h" +#include "graphics/l10n/Strings.h" #include "graphics/TimeFormatters.h" #include "graphics/emotes.h" #include "main.h" @@ -75,6 +76,15 @@ void scrollDown() void drawStringWithEmotes(OLEDDisplay *display, int x, int y, const std::string &line, const Emote *emotes, int emoteCount) { +#if defined(ST7735_CS) || defined(ST7789_CS) || defined(ILI9341_DRIVER) || defined(ILI9342_DRIVER) || defined(ST7701_CS) || \ + defined(HX8357_CS) || defined(ILI9488_CS) || defined(ST7796_CS) || defined(RAK14014) || defined(USE_ST7789) || \ + defined(USE_ST7796) + if (meshtastic::l10n::currentDisplayLanguage == meshtastic_Language_SIMPLIFIED_CHINESE || + meshtastic::l10n::currentDisplayLanguage == meshtastic_Language_TRADITIONAL_CHINESE) { + screen->drawTextL10n(x, y, line.c_str()); + return; + } +#endif graphics::EmoteRenderer::drawStringWithEmotes(display, x, y, line, FONT_HEIGHT_SMALL, emotes, emoteCount); } diff --git a/src/graphics/fonts/OLEDDisplayFontsZH.cpp b/src/graphics/fonts/OLEDDisplayFontsZH.cpp new file mode 100644 index 000000000..060b7054f --- /dev/null +++ b/src/graphics/fonts/OLEDDisplayFontsZH.cpp @@ -0,0 +1,46 @@ +// Chinese font data (VLW format) for OLEDDisplay-based TFT rendering via LovyanGFX. +// +// HOW TO GENERATE FONT DATA: +// +// Option 1: Adafruit GFX fontconvert (simpler, fewer glyphs) +// brew install adafruitnrf52-gfx-fontconvert # macOS +// fontconvert NotoSansSC-Regular.ttf 16 > zh_cn_16.h +// The output is a GFXfont struct. LovyanGFX can use this via ft_gfx type. +// +// Option 2: LovyanGFX VLW format (recommended, more features) +// Use the LovyanGFX font converter script to generate a .vlw file from TTF +// Then embed the .vlw binary as a byte array using xxd: +// xxd -i zh_font_16.vlw > zh_font_16_data.h +// +// Option 3: Use BDF fonts (free, text-based) +// WenQuanYi bitmap fonts: http://wenq.org/ +// Convert BDF to VLW using LovyanGFX tools +// +// FONT SIZE GUIDELINES: +// - 16px: Minimum readable Chinese on 320x240 TFT (~20 chars per line) +// - 20px: Better readability, ~16 chars per line +// - 24px: Title/header text, ~13 chars per line +// +// CHARACTER SET RECOMMENDATIONS: +// - GB2312 Level 1: 3755 commonly used simplified Chinese characters +// - Plus ASCII 0x20-0x7E for punctuation and Latin characters +// +// PLACEHOLDER DATA - Replace with generated font binary: + +#include "OLEDDisplayFontsZH.h" + +// Placeholder font data. +// Replace with actual generated VLW/Gfx font binary from a Chinese TTF file. +// The array below is intentionally minimal - it won't render any characters +// until real font data is provided. +const uint8_t FontZH_CN_16[] = { + // VLW font file magic header would go here + // Format: 0x00 0x00 ... + // Replace with output from: xxd -i your_chinese_font.vlw +}; +const uint32_t FontZH_CN_16_size = sizeof(FontZH_CN_16); + +const uint8_t FontZH_TW_16[] = { + // Traditional Chinese VLW font data placeholder +}; +const uint32_t FontZH_TW_16_size = sizeof(FontZH_TW_16); diff --git a/src/graphics/fonts/OLEDDisplayFontsZH.h b/src/graphics/fonts/OLEDDisplayFontsZH.h new file mode 100644 index 000000000..ecfe2610b --- /dev/null +++ b/src/graphics/fonts/OLEDDisplayFontsZH.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +// Chinese Simplified font - VLW format (to be generated from TTF) +// +// HOW TO GENERATE: +// 1. Download NotoSansSC-Regular.ttf (or any open-source Chinese font) +// 2. Use Adafruit fontconvert tool: +// ./fontconvert NotoSansSC-Regular.ttf 16 > zh_cn_16_pt.h +// 3. Copy the resulting binary data (GFXfont struct bytes) into the array below +// +// For LovyanGFX VLW font format, use: +// - The binary VLW file can be embedded as a const uint8_t[] array +// - Loaded with tft->loadFont(zh_font_data_array) + +extern const uint8_t FontZH_CN_16[]; +extern const uint32_t FontZH_CN_16_size; + +// Chinese Traditional font (VLW format) +extern const uint8_t FontZH_TW_16[]; +extern const uint32_t FontZH_TW_16_size; diff --git a/src/graphics/l10n/Strings.cpp b/src/graphics/l10n/Strings.cpp new file mode 100644 index 000000000..5a2adf41e --- /dev/null +++ b/src/graphics/l10n/Strings.cpp @@ -0,0 +1,20 @@ +#include "Strings.h" +#include "Strings_EN.h" +#include "Strings_ZH.h" + +namespace meshtastic +{ +namespace l10n +{ + +meshtastic_Language currentDisplayLanguage = meshtastic_Language_ENGLISH; + +const char *getString(StringKey key, meshtastic_Language lang) +{ + if (lang == meshtastic_Language_SIMPLIFIED_CHINESE || lang == meshtastic_Language_TRADITIONAL_CHINESE) + return getStringZH(key); + return getStringEN(key); +} + +} // namespace l10n +} // namespace meshtastic diff --git a/src/graphics/l10n/Strings.h b/src/graphics/l10n/Strings.h new file mode 100644 index 000000000..6464e3877 --- /dev/null +++ b/src/graphics/l10n/Strings.h @@ -0,0 +1,138 @@ +#pragma once + +#include "mesh/generated/meshtastic/device_ui.pb.h" + +namespace meshtastic +{ +namespace l10n +{ + +enum class StringKey : uint8_t { + Back, + OK, + GotIt, + WelcomeToMeshtastic, + SwipeToNavigate, + ClickToNavigate, + UseSelectButton, + DeviceRole, + RadioPreset, + FrequencySlot, + LoRaRegion, + Client, + ClientMute, + LostAndFound, + Tracker, + Hour12, + Hour24, + ClockAction, + ClockFace, + TimeFormat, + Timezone, + MessageAction, + Reply, + ViewChats, + UnmuteChannel, + MuteChannel, + Delete, + ReadAloud, + Brightness, + FrameVisibility, + DisplayUnits, + MessageBubbles, + Theme, + Reboot, + Shutdown, + SwitchToMUI, + DisplayOptions, + Language, + Chinese, + English, + NoGPS, + GPSoff, + GPSnotPresent, + GPSisDisabled, + Node, + Hop, + USB, + BT_off, + WiFi, + WiFiNotConnected, + WiFiConnected, + SSID, + LoRaInfo, + LoRa, + Role, + Freq, + ChUtil, + System, + Heap, + PSRAM, + Flash, + SD, + Ver, + Up, + ClientApp, + NoConnected, + LOCKED, + ConnectToUnlock, + Battery, + Resuming, + LastHeard, + HopsSig, + HopsSignal, + Nodes, + Distance, + Bearings, + CreatingSSLCert, + PleaseWait, + CriticalFault, + ForHelpVisit, + Updating, + PleaseBePatient, + RebootInProgress, + PowerMenu, + SystemMenu, + ScreenOptions, + WifiToggle, + BluetoothToggle, + NodeDbReset, + ManageNode, + RemoveFavorite, + AddFavorite, + TestMenu, + TraceRoute, + ThrottleMessage, + MessageViewMode, + DeleteMessages, + NodeNameLength, + GPSMenu, + GPSToggle, + GPSFormat, + GPSSmartPosition, + GPSUpdateInterval, + GPSPositionBroadcast, + CompassNorth, + BuzzerMode, + HamModeConfirm, + LicensedToNormal, + KeyVerification, + FinalPrompt, + RegionUnset, + SelectRegionMessage, + Rebooting, + COUNT +}; + +extern meshtastic_Language currentDisplayLanguage; + +const char *getString(StringKey key, meshtastic_Language lang); + +inline const char *getString(StringKey key) +{ + extern meshtastic_Language currentDisplayLanguage; + return getString(key, currentDisplayLanguage); +} + +} // namespace l10n +} // namespace meshtastic diff --git a/src/graphics/l10n/Strings_EN.h b/src/graphics/l10n/Strings_EN.h new file mode 100644 index 000000000..c8ec3c12a --- /dev/null +++ b/src/graphics/l10n/Strings_EN.h @@ -0,0 +1,360 @@ +#pragma once + +#include "Strings.h" + +namespace meshtastic +{ +namespace l10n +{ + +// English string table +// The strings are stored as const arrays to save RAM on constrained devices. +// Each locale file defines the same set of strings in its own language. + +static const char STR_BACK_EN[] = "Back"; +static const char STR_OK_EN[] = "OK"; +static const char STR_GOT_IT_EN[] = "Got it!"; +static const char STR_WELCOME_TO_MESHTASTIC_EN[] = "Welcome to Meshtastic!"; +static const char STR_SWIPE_TO_NAVIGATE_EN[] = "Swipe to navigate."; +static const char STR_CLICK_TO_NAVIGATE_EN[] = "Click to navigate."; +static const char STR_USE_SELECT_BUTTON_EN[] = "Use the Select button."; +static const char STR_DEVICE_ROLE_EN[] = "Device Role"; +static const char STR_RADIO_PRESET_EN[] = "Radio Preset"; +static const char STR_FREQUENCY_SLOT_EN[] = "Frequency Slot"; +static const char STR_LORA_REGION_EN[] = "LoRa Region"; +static const char STR_CLIENT_EN[] = "Client"; +static const char STR_CLIENT_MUTE_EN[] = "Client Mute"; +static const char STR_LOST_AND_FOUND_EN[] = "Lost and Found"; +static const char STR_TRACKER_EN[] = "Tracker"; +static const char STR_12HOUR_EN[] = "12-hour"; +static const char STR_24HOUR_EN[] = "24-hour"; +static const char STR_CLOCK_ACTION_EN[] = "Clock Action"; +static const char STR_CLOCK_FACE_EN[] = "Clock Face"; +static const char STR_TIME_FORMAT_EN[] = "Time Format"; +static const char STR_TIMEZONE_EN[] = "Timezone"; +static const char STR_MESSAGE_ACTION_EN[] = "Message Action"; +static const char STR_REPLY_EN[] = "Reply"; +static const char STR_VIEW_CHATS_EN[] = "View Chats"; +static const char STR_UNMUTE_CHANNEL_EN[] = "Unmute Channel"; +static const char STR_MUTE_CHANNEL_EN[] = "Mute Channel"; +static const char STR_DELETE_EN[] = "Delete"; +static const char STR_READ_ALOUD_EN[] = "Read Aloud"; +static const char STR_BRIGHTNESS_EN[] = "Brightness"; +static const char STR_FRAME_VISIBILITY_EN[] = "Frame Visibility"; +static const char STR_DISPLAY_UNITS_EN[] = "Display Units"; +static const char STR_MESSAGE_BUBBLES_EN[] = "Message Bubbles"; +static const char STR_THEME_EN[] = "Theme"; +static const char STR_REBOOT_EN[] = "Reboot"; +static const char STR_SHUTDOWN_EN[] = "Shutdown"; +static const char STR_SWITCH_TO_MUI_EN[] = "Switch to MUI"; +static const char STR_DISPLAY_OPTIONS_EN[] = "Display Options"; +static const char STR_LANGUAGE_EN[] = "Language"; +static const char STR_CHINESE_EN[] = "Chinese"; +static const char STR_ENGLISH_EN[] = "English"; +static const char STR_NO_GPS_EN[] = "No GPS"; +static const char STR_GPS_OFF_EN[] = "GPS off"; +static const char STR_GPS_NOT_PRESENT_EN[] = "GPS not present"; +static const char STR_GPS_IS_DISABLED_EN[] = "GPS is disabled"; +static const char STR_NODE_EN[] = "Node"; +static const char STR_HOP_EN[] = "Hop:"; +static const char STR_USB_EN[] = "USB"; +static const char STR_BT_OFF_EN[] = "BT off"; +static const char STR_WIFI_EN[] = "WiFi"; +static const char STR_WIFI_NOT_CONNECTED_EN[] = "WiFi: Not Connected"; +static const char STR_WIFI_CONNECTED_EN[] = "WiFi: Connected"; +static const char STR_SSID_EN[] = "SSID:"; +static const char STR_LORA_INFO_EN[] = "LoRa Info"; +static const char STR_LORA_EN[] = "LoRa"; +static const char STR_ROLE_EN[] = "Role:"; +static const char STR_FREQ_EN[] = "Freq:"; +static const char STR_CHUTIL_EN[] = "ChUtil"; +static const char STR_SYSTEM_EN[] = "System"; +static const char STR_HEAP_EN[] = "Heap:"; +static const char STR_PSRAM_EN[] = "PSRAM:"; +static const char STR_FLASH_EN[] = "Flash:"; +static const char STR_SD_EN[] = "SD:"; +static const char STR_VER_EN[] = "Ver:"; +static const char STR_UP_EN[] = "Up:"; +static const char STR_CLIENT_APP_EN[] = "Client"; +static const char STR_NO_CONNECTED_EN[] = "No Connected"; +static const char STR_LOCKED_EN[] = "LOCKED"; +static const char STR_CONNECT_TO_UNLOCK_EN[] = "Connect to unlock"; +static const char STR_BATTERY_EN[] = "Battery"; +static const char STR_RESUMING_EN[] = "Resuming..."; +static const char STR_LAST_HEARD_EN[] = "Last Heard"; +static const char STR_HOPS_SIG_EN[] = "Hops/Sig"; +static const char STR_HOPS_SIGNAL_EN[] = "Hops/Signal"; +static const char STR_NODES_EN[] = "Nodes"; +static const char STR_DISTANCE_EN[] = "Distance"; +static const char STR_BEARINGS_EN[] = "Bearings"; +static const char STR_CREATING_SSL_EN[] = "Creating SSL certificate"; +static const char STR_PLEASE_WAIT_EN[] = "Please wait . . ."; +static const char STR_CRITICAL_FAULT_EN[] = "Critical fault"; +static const char STR_FOR_HELP_VISIT_EN[] = "For help, please visit"; +static const char STR_UPDATING_EN[] = "Updating"; +static const char STR_PLEASE_BE_PATIENT_EN[] = "Please be patient and do not power off."; +static const char STR_REBOOT_IN_PROGRESS_EN[] = "Reboot in progress..."; +static const char STR_POWER_MENU_EN[] = "Power"; +static const char STR_SYSTEM_MENU_EN[] = "System"; +static const char STR_SCREEN_OPTIONS_EN[] = "Screen"; +static const char STR_WIFI_TOGGLE_EN[] = "WiFi"; +static const char STR_BLUETOOTH_TOGGLE_EN[] = "Bluetooth"; +static const char STR_NODE_DB_RESET_EN[] = "Reset NodeDB"; +static const char STR_MANAGE_NODE_EN[] = "Manage Node"; +static const char STR_REMOVE_FAVORITE_EN[] = "Remove Favorite"; +static const char STR_ADD_FAVORITE_EN[] = "Add Favorite"; +static const char STR_TEST_MENU_EN[] = "Test"; +static const char STR_TRACE_ROUTE_EN[] = "Trace Route"; +static const char STR_THROTTLE_MSG_EN[] = "Message Throttled"; +static const char STR_MSG_VIEW_MODE_EN[] = "View Mode"; +static const char STR_DELETE_MESSAGES_EN[] = "Delete Messages"; +static const char STR_NODE_NAME_LENGTH_EN[] = "Name Length"; +static const char STR_GPS_MENU_EN[] = "GPS"; +static const char STR_GPS_TOGGLE_EN[] = "GPS Toggle"; +static const char STR_GPS_FORMAT_EN[] = "GPS Format"; +static const char STR_GPS_SMART_POS_EN[] = "Smart Position"; +static const char STR_GPS_UPDATE_INTERVAL_EN[] = "Update Interval"; +static const char STR_GPS_POS_BCAST_EN[] = "Position Broadcast"; +static const char STR_COMPASS_NORTH_EN[] = "Compass North"; +static const char STR_BUZZER_MODE_EN[] = "Buzzer"; +static const char STR_HAM_MODE_EN[] = "Ham Mode"; +static const char STR_LICENSED_NORMAL_EN[] = "Normal Mode"; +static const char STR_KEY_VERIFY_EN[] = "Verify Key"; +static const char STR_REGION_UNSET_EN[] = "Select a Region"; +static const char STR_SELECT_REGION_EN[] = "Please select your LoRa region:"; +static const char STR_REBOOTING_EN[] = "Rebooting..."; + +inline const char *getStringEN(StringKey key) +{ + switch (key) { + case StringKey::Back: + return STR_BACK_EN; + case StringKey::OK: + return STR_OK_EN; + case StringKey::GotIt: + return STR_GOT_IT_EN; + case StringKey::WelcomeToMeshtastic: + return STR_WELCOME_TO_MESHTASTIC_EN; + case StringKey::SwipeToNavigate: + return STR_SWIPE_TO_NAVIGATE_EN; + case StringKey::ClickToNavigate: + return STR_CLICK_TO_NAVIGATE_EN; + case StringKey::UseSelectButton: + return STR_USE_SELECT_BUTTON_EN; + case StringKey::DeviceRole: + return STR_DEVICE_ROLE_EN; + case StringKey::RadioPreset: + return STR_RADIO_PRESET_EN; + case StringKey::FrequencySlot: + return STR_FREQUENCY_SLOT_EN; + case StringKey::LoRaRegion: + return STR_LORA_REGION_EN; + case StringKey::Client: + return STR_CLIENT_EN; + case StringKey::ClientMute: + return STR_CLIENT_MUTE_EN; + case StringKey::LostAndFound: + return STR_LOST_AND_FOUND_EN; + case StringKey::Tracker: + return STR_TRACKER_EN; + case StringKey::Hour12: + return STR_12HOUR_EN; + case StringKey::Hour24: + return STR_24HOUR_EN; + case StringKey::ClockAction: + return STR_CLOCK_ACTION_EN; + case StringKey::ClockFace: + return STR_CLOCK_FACE_EN; + case StringKey::TimeFormat: + return STR_TIME_FORMAT_EN; + case StringKey::Timezone: + return STR_TIMEZONE_EN; + case StringKey::MessageAction: + return STR_MESSAGE_ACTION_EN; + case StringKey::Reply: + return STR_REPLY_EN; + case StringKey::ViewChats: + return STR_VIEW_CHATS_EN; + case StringKey::UnmuteChannel: + return STR_UNMUTE_CHANNEL_EN; + case StringKey::MuteChannel: + return STR_MUTE_CHANNEL_EN; + case StringKey::Delete: + return STR_DELETE_EN; + case StringKey::ReadAloud: + return STR_READ_ALOUD_EN; + case StringKey::Brightness: + return STR_BRIGHTNESS_EN; + case StringKey::FrameVisibility: + return STR_FRAME_VISIBILITY_EN; + case StringKey::DisplayUnits: + return STR_DISPLAY_UNITS_EN; + case StringKey::MessageBubbles: + return STR_MESSAGE_BUBBLES_EN; + case StringKey::Theme: + return STR_THEME_EN; + case StringKey::Reboot: + return STR_REBOOT_EN; + case StringKey::Shutdown: + return STR_SHUTDOWN_EN; + case StringKey::SwitchToMUI: + return STR_SWITCH_TO_MUI_EN; + case StringKey::DisplayOptions: + return STR_DISPLAY_OPTIONS_EN; + case StringKey::Language: + return STR_LANGUAGE_EN; + case StringKey::Chinese: + return STR_CHINESE_EN; + case StringKey::English: + return STR_ENGLISH_EN; + case StringKey::NoGPS: + return STR_NO_GPS_EN; + case StringKey::GPSoff: + return STR_GPS_OFF_EN; + case StringKey::GPSnotPresent: + return STR_GPS_NOT_PRESENT_EN; + case StringKey::GPSisDisabled: + return STR_GPS_IS_DISABLED_EN; + case StringKey::Node: + return STR_NODE_EN; + case StringKey::Hop: + return STR_HOP_EN; + case StringKey::USB: + return STR_USB_EN; + case StringKey::BT_off: + return STR_BT_OFF_EN; + case StringKey::WiFi: + return STR_WIFI_EN; + case StringKey::WiFiNotConnected: + return STR_WIFI_NOT_CONNECTED_EN; + case StringKey::WiFiConnected: + return STR_WIFI_CONNECTED_EN; + case StringKey::SSID: + return STR_SSID_EN; + case StringKey::LoRaInfo: + return STR_LORA_INFO_EN; + case StringKey::LoRa: + return STR_LORA_EN; + case StringKey::Role: + return STR_ROLE_EN; + case StringKey::Freq: + return STR_FREQ_EN; + case StringKey::ChUtil: + return STR_CHUTIL_EN; + case StringKey::System: + return STR_SYSTEM_EN; + case StringKey::Heap: + return STR_HEAP_EN; + case StringKey::PSRAM: + return STR_PSRAM_EN; + case StringKey::Flash: + return STR_FLASH_EN; + case StringKey::SD: + return STR_SD_EN; + case StringKey::Ver: + return STR_VER_EN; + case StringKey::Up: + return STR_UP_EN; + case StringKey::ClientApp: + return STR_CLIENT_APP_EN; + case StringKey::NoConnected: + return STR_NO_CONNECTED_EN; + case StringKey::LOCKED: + return STR_LOCKED_EN; + case StringKey::ConnectToUnlock: + return STR_CONNECT_TO_UNLOCK_EN; + case StringKey::Battery: + return STR_BATTERY_EN; + case StringKey::Resuming: + return STR_RESUMING_EN; + case StringKey::LastHeard: + return STR_LAST_HEARD_EN; + case StringKey::HopsSig: + return STR_HOPS_SIG_EN; + case StringKey::HopsSignal: + return STR_HOPS_SIGNAL_EN; + case StringKey::Nodes: + return STR_NODES_EN; + case StringKey::Distance: + return STR_DISTANCE_EN; + case StringKey::Bearings: + return STR_BEARINGS_EN; + case StringKey::CreatingSSLCert: + return STR_CREATING_SSL_EN; + case StringKey::PleaseWait: + return STR_PLEASE_WAIT_EN; + case StringKey::CriticalFault: + return STR_CRITICAL_FAULT_EN; + case StringKey::ForHelpVisit: + return STR_FOR_HELP_VISIT_EN; + case StringKey::Updating: + return STR_UPDATING_EN; + case StringKey::PleaseBePatient: + return STR_PLEASE_BE_PATIENT_EN; + case StringKey::RebootInProgress: + return STR_REBOOT_IN_PROGRESS_EN; + case StringKey::PowerMenu: + return STR_POWER_MENU_EN; + case StringKey::SystemMenu: + return STR_SYSTEM_MENU_EN; + case StringKey::ScreenOptions: + return STR_SCREEN_OPTIONS_EN; + case StringKey::WifiToggle: + return STR_WIFI_TOGGLE_EN; + case StringKey::BluetoothToggle: + return STR_BLUETOOTH_TOGGLE_EN; + case StringKey::NodeDbReset: + return STR_NODE_DB_RESET_EN; + case StringKey::ManageNode: + return STR_MANAGE_NODE_EN; + case StringKey::RemoveFavorite: + return STR_REMOVE_FAVORITE_EN; + case StringKey::AddFavorite: + return STR_ADD_FAVORITE_EN; + case StringKey::TestMenu: + return STR_TEST_MENU_EN; + case StringKey::TraceRoute: + return STR_TRACE_ROUTE_EN; + case StringKey::ThrottleMessage: + return STR_THROTTLE_MSG_EN; + case StringKey::MessageViewMode: + return STR_MSG_VIEW_MODE_EN; + case StringKey::DeleteMessages: + return STR_DELETE_MESSAGES_EN; + case StringKey::NodeNameLength: + return STR_NODE_NAME_LENGTH_EN; + case StringKey::GPSMenu: + return STR_GPS_MENU_EN; + case StringKey::GPSToggle: + return STR_GPS_TOGGLE_EN; + case StringKey::GPSFormat: + return STR_GPS_FORMAT_EN; + case StringKey::GPSSmartPosition: + return STR_GPS_SMART_POS_EN; + case StringKey::GPSUpdateInterval: + return STR_GPS_UPDATE_INTERVAL_EN; + case StringKey::GPSPositionBroadcast: + return STR_GPS_POS_BCAST_EN; + case StringKey::CompassNorth: + return STR_COMPASS_NORTH_EN; + case StringKey::BuzzerMode: + return STR_BUZZER_MODE_EN; + case StringKey::HamModeConfirm: + return STR_HAM_MODE_EN; + case StringKey::LicensedToNormal: + return STR_LICENSED_NORMAL_EN; + case StringKey::KeyVerification: + return STR_KEY_VERIFY_EN; + case StringKey::RegionUnset: + return STR_REGION_UNSET_EN; + case StringKey::SelectRegionMessage: + return STR_SELECT_REGION_EN; + case StringKey::Rebooting: + return STR_REBOOTING_EN; + default: + return "?"; + } +} + +} // namespace l10n +} // namespace meshtastic diff --git a/src/graphics/l10n/Strings_ZH.h b/src/graphics/l10n/Strings_ZH.h new file mode 100644 index 000000000..85c6cb30f --- /dev/null +++ b/src/graphics/l10n/Strings_ZH.h @@ -0,0 +1,356 @@ +#pragma once + +#include "Strings.h" + +namespace meshtastic +{ +namespace l10n +{ + +static const char STR_BACK_ZH[] = "\xe8\xbf\x94\xe5\x9b\x9e"; +static const char STR_OK_ZH[] = "\xe7\xa1\xae\xe5\xae\x9a"; +static const char STR_GOT_IT_ZH[] = "\xe7\x9f\xa5\xe9\x81\x93\xe4\xba\x86"; +static const char STR_WELCOME_TO_MESHTASTIC_ZH[] = "\xe6\xac\xa2\xe8\xbf\x8e\xe4\xbd\xbf\xe7\x94\xa8 Meshtastic\xef\xbc\x81"; +static const char STR_SWIPE_TO_NAVIGATE_ZH[] = "\xe6\xbb\x91\xe5\x8a\xa8\xe4\xbb\xa5\xe5\xaf\xbc\xe8\x88\xaa\xe3\x80\x82"; +static const char STR_CLICK_TO_NAVIGATE_ZH[] = "\xe7\x82\xb9\xe5\x87\xbb\xe4\xbb\xa5\xe5\xaf\xbc\xe8\x88\xaa\xe3\x80\x82"; +static const char STR_USE_SELECT_BUTTON_ZH[] = "\xe4\xbd\xbf\xe7\x94\xa8\xe9\x80\x89\xe6\x8b\xa9\xe6\x8c\x89\xe9\x92\xae\xe3\x80\x82"; +static const char STR_DEVICE_ROLE_ZH[] = "\xe8\xae\xbe\xe5\xa4\x87\xe8\xa7\x92\xe8\x89\xb2"; +static const char STR_RADIO_PRESET_ZH[] = "\xe6\x97\xa0\xe7\xba\xbf\xe9\xa2\x84\xe8\xae\xbe"; +static const char STR_FREQUENCY_SLOT_ZH[] = "\xe9\xa2\x91\xe7\x8e\x87\xe6\xa7\xbd\xe4\xbd\x8d"; +static const char STR_LORA_REGION_ZH[] = "LoRa \xe5\x9c\xb0\xe5\x8c\xba"; +static const char STR_CLIENT_ZH[] = "\xe5\xae\xa2\xe6\x88\xb7\xe7\xab\xaf"; +static const char STR_CLIENT_MUTE_ZH[] = "\xe9\x9d\x99\xe9\x9f\xb3\xe5\xae\xa2\xe6\x88\xb7\xe7\xab\xaf"; +static const char STR_LOST_AND_FOUND_ZH[] = "\xe5\xa4\xb1\xe7\x89\xa9\xe6\x8b\x9b\xe9\xa2\x86"; +static const char STR_TRACKER_ZH[] = "\xe8\xbf\xbd\xe8\xb8\xaa\xe5\x99\xa8"; +static const char STR_12HOUR_ZH[] = "12 \xe5\xb0\x8f\xe6\x97\xb6\xe5\x88\xb6"; +static const char STR_24HOUR_ZH[] = "24 \xe5\xb0\x8f\xe6\x97\xb6\xe5\x88\xb6"; +static const char STR_CLOCK_ACTION_ZH[] = "\xe6\x97\xb6\xe9\x92\x9f\xe6\x93\x8d\xe4\xbd\x9c"; +static const char STR_CLOCK_FACE_ZH[] = "\xe6\x97\xb6\xe9\x92\x9f\xe6\xa0\xb7\xe5\xbc\x8f"; +static const char STR_TIME_FORMAT_ZH[] = "\xe6\x97\xb6\xe9\x97\xb4\xe6\xa0\xbc\xe5\xbc\x8f"; +static const char STR_TIMEZONE_ZH[] = "\xe6\x97\xb6\xe5\x8c\xba"; +static const char STR_MESSAGE_ACTION_ZH[] = "\xe6\xb6\x88\xe6\x81\xaf\xe6\x93\x8d\xe4\xbd\x9c"; +static const char STR_REPLY_ZH[] = "\xe5\x9b\x9e\xe5\xa4\x8d"; +static const char STR_VIEW_CHATS_ZH[] = "\xe6\x9f\xa5\xe7\x9c\x8b\xe8\x81\x8a\xe5\xa4\xa9"; +static const char STR_UNMUTE_CHANNEL_ZH[] = "\xe5\x8f\x96\xe6\xb6\x88\xe9\x9d\x99\xe9\x9f\xb3"; +static const char STR_MUTE_CHANNEL_ZH[] = "\xe9\x9d\x99\xe9\x9f\xb3\xe9\xa2\x91\xe9\x81\x93"; +static const char STR_DELETE_ZH[] = "\xe5\x88\xa0\xe9\x99\xa4"; +static const char STR_READ_ALOUD_ZH[] = "\xe5\xa4\xa7\xe5\xa3\xb0\xe6\x9c\x97\xe8\xaf\xbb"; +static const char STR_BRIGHTNESS_ZH[] = "\xe4\xba\xae\xe5\xba\xa6"; +static const char STR_FRAME_VISIBILITY_ZH[] = "\xe9\xa1\xb5\xe9\x9d\xa2\xe5\x8f\xaf\xe8\xa7\x81\xe6\x80\xa7"; +static const char STR_DISPLAY_UNITS_ZH[] = "\xe6\x98\xbe\xe7\xa4\xba\xe5\x8d\x95\xe4\xbd\x8d"; +static const char STR_MESSAGE_BUBBLES_ZH[] = "\xe6\xb6\x88\xe6\x81\xaf\xe6\xb0\x94\xe6\xb3\xa1"; +static const char STR_THEME_ZH[] = "\xe4\xb8\xbb\xe9\xa2\x98"; +static const char STR_REBOOT_ZH[] = "\xe9\x87\x8d\xe5\x90\xaf"; +static const char STR_SHUTDOWN_ZH[] = "\xe5\x85\xb3\xe6\x9c\xba"; +static const char STR_SWITCH_TO_MUI_ZH[] = "\xe5\x88\x87\xe6\x8d\xa2\xe5\x88\xb0 MUI"; +static const char STR_DISPLAY_OPTIONS_ZH[] = "\xe6\x98\xbe\xe7\xa4\xba\xe9\x80\x89\xe9\xa1\xb9"; +static const char STR_LANGUAGE_ZH[] = "\xe8\xaf\xad\xe8\xa8\x80"; +static const char STR_CHINESE_ZH[] = "\xe4\xb8\xad\xe6\x96\x87"; +static const char STR_ENGLISH_ZH[] = "\xe8\x8b\xb1\xe6\x96\x87"; +static const char STR_NO_GPS_ZH[] = "\xe6\x97\xa0 GPS"; +static const char STR_GPS_OFF_ZH[] = "GPS \xe5\xb7\xb2\xe5\x85\xb3\xe9\x97\xad"; +static const char STR_GPS_NOT_PRESENT_ZH[] = "GPS \xe4\xb8\x8d\xe5\xad\x98\xe5\x9c\xa8"; +static const char STR_GPS_IS_DISABLED_ZH[] = "GPS \xe5\xb7\xb2\xe7\xa6\x81\xe7\x94\xa8"; +static const char STR_NODE_ZH[] = "\xe8\x8a\x82\xe7\x82\xb9"; +static const char STR_HOP_ZH[] = "\xe8\xb7\xb3\xe6\x95\xb0:"; +static const char STR_USB_ZH[] = "USB"; +static const char STR_BT_OFF_ZH[] = "\xe8\x93\x9d\xe7\x89\x99\xe5\xb7\xb2\xe5\x85\xb3"; +static const char STR_WIFI_ZH[] = "WiFi"; +static const char STR_WIFI_NOT_CONNECTED_ZH[] = "WiFi: \xe6\x9c\xaa\xe8\xbf\x9e\xe6\x8e\xa5"; +static const char STR_WIFI_CONNECTED_ZH[] = "WiFi: \xe5\xb7\xb2\xe8\xbf\x9e\xe6\x8e\xa5"; +static const char STR_SSID_ZH[] = "SSID:"; +static const char STR_LORA_INFO_ZH[] = "LoRa \xe4\xbf\xa1\xe6\x81\xaf"; +static const char STR_LORA_ZH[] = "LoRa"; +static const char STR_ROLE_ZH[] = "\xe8\xa7\x92\xe8\x89\xb2:"; +static const char STR_FREQ_ZH[] = "\xe9\xa2\x91\xe7\x8e\x87:"; +static const char STR_CHUTIL_ZH[] = "\xe4\xbf\xa1\xe9\x81\x93\xe5\x88\xa9\xe7\x94\xa8"; +static const char STR_SYSTEM_ZH[] = "\xe7\xb3\xbb\xe7\xbb\x9f"; +static const char STR_HEAP_ZH[] = "\xe5\xa0\x86:"; +static const char STR_PSRAM_ZH[] = "PSRAM:"; +static const char STR_FLASH_ZH[] = "Flash:"; +static const char STR_SD_ZH[] = "SD:"; +static const char STR_VER_ZH[] = "\xe7\x89\x88\xe6\x9c\xac:"; +static const char STR_UP_ZH[] = "\xe8\xbf\x90\xe8\xa1\x8c:"; +static const char STR_CLIENT_APP_ZH[] = "\xe5\xae\xa2\xe6\x88\xb7\xe7\xab\xaf"; +static const char STR_NO_CONNECTED_ZH[] = "\xe6\x9c\xaa\xe8\xbf\x9e\xe6\x8e\xa5"; +static const char STR_LOCKED_ZH[] = "\xe5\xb7\xb2\xe9\x94\x81\xe5\xae\x9a"; +static const char STR_CONNECT_TO_UNLOCK_ZH[] = "\xe8\xbf\x9e\xe6\x8e\xa5\xe4\xbb\xa5\xe8\xa7\xa3\xe9\x94\x81"; +static const char STR_BATTERY_ZH[] = "\xe7\x94\xb5\xe6\xb1\xa0"; +static const char STR_RESUMING_ZH[] = "\xe6\xad\xa3\xe5\x9c\xa8\xe6\x81\xa2\xe5\xa4\x8d..."; +static const char STR_LAST_HEARD_ZH[] = "\xe6\x9c\x80\xe5\x90\x8e\xe5\x90\xac\xe5\x88\xb0"; +static const char STR_HOPS_SIG_ZH[] = "\xe8\xb7\xb3\xe6\x95\xb0/\xe4\xbf\xa1\xe5\x8f\xb7"; +static const char STR_HOPS_SIGNAL_ZH[] = "\xe8\xb7\xb3\xe6\x95\xb0/\xe4\xbf\xa1\xe5\x8f\xb7"; +static const char STR_NODES_ZH[] = "\xe8\x8a\x82\xe7\x82\xb9"; +static const char STR_DISTANCE_ZH[] = "\xe8\xb7\x9d\xe7\xa6\xbb"; +static const char STR_BEARINGS_ZH[] = "\xe6\x96\xb9\xe4\xbd\x8d"; +static const char STR_CREATING_SSL_ZH[] = "\xe6\xad\xa3\xe5\x9c\xa8\xe5\x88\x9b\xe5\xbb\xba SSL \xe8\xaf\x81\xe4\xb9\xa6"; +static const char STR_PLEASE_WAIT_ZH[] = "\xe8\xaf\xb7\xe7\xa8\x8d\xe5\x80\x99 . . ."; +static const char STR_CRITICAL_FAULT_ZH[] = "\xe4\xb8\xa5\xe9\x87\x8d\xe6\x95\x85\xe9\x9a\x9c"; +static const char STR_FOR_HELP_VISIT_ZH[] = "\xe8\xaf\xb7\xe8\xae\xbf\xe9\x97\xae\x4ee5\xb8\xae\xe5\x8a\xa9"; +static const char STR_UPDATING_ZH[] = "\xe6\xad\xa3\xe5\x9c\xa8\xe6\x9b\xb4\xe6\x96\xb0"; +static const char STR_PLEASE_BE_PATIENT_ZH[] = "\xe8\xaf\xb7\xe8\x80\x90\xe5\xbf\x83\xe7\xad\x89\xe5\xbe\x85\xef\xbc\x8c\xe4\xb8\x8d\xe8\xa6\x81\xe5\x85\xb3\xe9\x97\xad\xe7\x94\xb5\xe6\xba\x90\xe3\x80\x82"; +static const char STR_REBOOT_IN_PROGRESS_ZH[] = "\xe6\xad\xa3\xe5\x9c\xa8\xe9\x87\x8d\xe5\x90\xaf..."; +static const char STR_POWER_MENU_ZH[] = "\xe7\x94\xb5\xe6\xba\x90"; +static const char STR_SYSTEM_MENU_ZH[] = "\xe7\xb3\xbb\xe7\xbb\x9f"; +static const char STR_SCREEN_OPTIONS_ZH[] = "\xe5\xb1\x8f\xe5\xb9\x95"; +static const char STR_WIFI_TOGGLE_ZH[] = "WiFi"; +static const char STR_BLUETOOTH_TOGGLE_ZH[] = "\xe8\x93\x9d\xe7\x89\x99"; +static const char STR_NODE_DB_RESET_ZH[] = "\xe9\x87\x8d\xe7\xbd\xae\xe8\x8a\x82\xe7\x82\xb9\xe5\xba\x93"; +static const char STR_MANAGE_NODE_ZH[] = "\xe7\xae\xa1\xe7\x90\x86\xe8\x8a\x82\xe7\x82\xb9"; +static const char STR_REMOVE_FAVORITE_ZH[] = "\xe5\x8f\x96\xe6\xb6\x88\xe6\x94\xb6\xe8\x97\x8f"; +static const char STR_ADD_FAVORITE_ZH[] = "\xe6\xb7\xbb\xe5\x8a\xa0\xe6\x94\xb6\xe8\x97\x8f"; +static const char STR_TEST_MENU_ZH[] = "\xe6\xb5\x8b\xe8\xaf\x95"; +static const char STR_TRACE_ROUTE_ZH[] = "\xe8\xb7\xaf\xe7\x94\xb1\xe8\xbf\xbd\xe8\xb8\xaa"; +static const char STR_THROTTLE_MSG_ZH[] = "\xe6\xb6\x88\xe6\x81\xaf\xe5\xb7\xb2\xe9\x99\x90\xe6\xb5\x81"; +static const char STR_MSG_VIEW_MODE_ZH[] = "\xe6\x9f\xa5\xe7\x9c\x8b\xe6\xa8\xa1\xe5\xbc\x8f"; +static const char STR_DELETE_MESSAGES_ZH[] = "\xe5\x88\xa0\xe9\x99\xa4\xe6\xb6\x88\xe6\x81\xaf"; +static const char STR_NODE_NAME_LENGTH_ZH[] = "\xe5\x90\x8d\xe7\xa7\xb0\xe9\x95\xbf\xe5\xba\xa6"; +static const char STR_GPS_MENU_ZH[] = "GPS"; +static const char STR_GPS_TOGGLE_ZH[] = "GPS \xe5\xbc\x80\xe5\x85\xb3"; +static const char STR_GPS_FORMAT_ZH[] = "GPS \xe6\xa0\xbc\xe5\xbc\x8f"; +static const char STR_GPS_SMART_POS_ZH[] = "\xe6\x99\xba\xe8\x83\xbd\xe5\xae\x9a\xe4\xbd\x8d"; +static const char STR_GPS_UPDATE_INTERVAL_ZH[] = "\xe6\x9b\xb4\xe6\x96\xb0\xe9\x97\xb4\xe9\x9a\x94"; +static const char STR_GPS_POS_BCAST_ZH[] = "\xe4\xbd\x8d\xe7\xbd\xae\xe5\xb9\xbf\xe6\x92\xad"; +static const char STR_COMPASS_NORTH_ZH[] = "\xe6\x8c\x87\xe5\x8d\x97\xe9\x92\x88"; +static const char STR_BUZZER_MODE_ZH[] = "\xe8\x9c\x82\xe9\xb8\xa3\xe5\x99\xa8"; +static const char STR_HAM_MODE_ZH[] = "Ham \xe6\xa8\xa1\xe5\xbc\x8f"; +static const char STR_LICENSED_NORMAL_ZH[] = "\xe6\x99\xae\xe9\x80\x9a\xe6\xa8\xa1\xe5\xbc\x8f"; +static const char STR_KEY_VERIFY_ZH[] = "\xe9\xaa\x8c\xe8\xaf\x81\xe5\xaf\x86\xe9\x92\xa5"; +static const char STR_REGION_UNSET_ZH[] = "\xe8\xaf\xb7\xe9\x80\x89\xe6\x8b\xa9\xe5\x9c\xb0\xe5\x8c\xba"; +static const char STR_SELECT_REGION_ZH[] = "\xe8\xaf\xb7\xe9\x80\x89\xe6\x8b\xa9\xe6\x82\xa8\xe7\x9a\x84 LoRa \xe5\x8c\xba\xe5\x9f\x9f:"; +static const char STR_REBOOTING_ZH[] = "\xe6\xad\xa3\xe5\x9c\xa8\xe9\x87\x8d\xe5\x90\xaf..."; + +inline const char *getStringZH(StringKey key) +{ + switch (key) { + case StringKey::Back: + return STR_BACK_ZH; + case StringKey::OK: + return STR_OK_ZH; + case StringKey::GotIt: + return STR_GOT_IT_ZH; + case StringKey::WelcomeToMeshtastic: + return STR_WELCOME_TO_MESHTASTIC_ZH; + case StringKey::SwipeToNavigate: + return STR_SWIPE_TO_NAVIGATE_ZH; + case StringKey::ClickToNavigate: + return STR_CLICK_TO_NAVIGATE_ZH; + case StringKey::UseSelectButton: + return STR_USE_SELECT_BUTTON_ZH; + case StringKey::DeviceRole: + return STR_DEVICE_ROLE_ZH; + case StringKey::RadioPreset: + return STR_RADIO_PRESET_ZH; + case StringKey::FrequencySlot: + return STR_FREQUENCY_SLOT_ZH; + case StringKey::LoRaRegion: + return STR_LORA_REGION_ZH; + case StringKey::Client: + return STR_CLIENT_ZH; + case StringKey::ClientMute: + return STR_CLIENT_MUTE_ZH; + case StringKey::LostAndFound: + return STR_LOST_AND_FOUND_ZH; + case StringKey::Tracker: + return STR_TRACKER_ZH; + case StringKey::Hour12: + return STR_12HOUR_ZH; + case StringKey::Hour24: + return STR_24HOUR_ZH; + case StringKey::ClockAction: + return STR_CLOCK_ACTION_ZH; + case StringKey::ClockFace: + return STR_CLOCK_FACE_ZH; + case StringKey::TimeFormat: + return STR_TIME_FORMAT_ZH; + case StringKey::Timezone: + return STR_TIMEZONE_ZH; + case StringKey::MessageAction: + return STR_MESSAGE_ACTION_ZH; + case StringKey::Reply: + return STR_REPLY_ZH; + case StringKey::ViewChats: + return STR_VIEW_CHATS_ZH; + case StringKey::UnmuteChannel: + return STR_UNMUTE_CHANNEL_ZH; + case StringKey::MuteChannel: + return STR_MUTE_CHANNEL_ZH; + case StringKey::Delete: + return STR_DELETE_ZH; + case StringKey::ReadAloud: + return STR_READ_ALOUD_ZH; + case StringKey::Brightness: + return STR_BRIGHTNESS_ZH; + case StringKey::FrameVisibility: + return STR_FRAME_VISIBILITY_ZH; + case StringKey::DisplayUnits: + return STR_DISPLAY_UNITS_ZH; + case StringKey::MessageBubbles: + return STR_MESSAGE_BUBBLES_ZH; + case StringKey::Theme: + return STR_THEME_ZH; + case StringKey::Reboot: + return STR_REBOOT_ZH; + case StringKey::Shutdown: + return STR_SHUTDOWN_ZH; + case StringKey::SwitchToMUI: + return STR_SWITCH_TO_MUI_ZH; + case StringKey::DisplayOptions: + return STR_DISPLAY_OPTIONS_ZH; + case StringKey::Language: + return STR_LANGUAGE_ZH; + case StringKey::Chinese: + return STR_CHINESE_ZH; + case StringKey::English: + return STR_ENGLISH_ZH; + case StringKey::NoGPS: + return STR_NO_GPS_ZH; + case StringKey::GPSoff: + return STR_GPS_OFF_ZH; + case StringKey::GPSnotPresent: + return STR_GPS_NOT_PRESENT_ZH; + case StringKey::GPSisDisabled: + return STR_GPS_IS_DISABLED_ZH; + case StringKey::Node: + return STR_NODE_ZH; + case StringKey::Hop: + return STR_HOP_ZH; + case StringKey::USB: + return STR_USB_ZH; + case StringKey::BT_off: + return STR_BT_OFF_ZH; + case StringKey::WiFi: + return STR_WIFI_ZH; + case StringKey::WiFiNotConnected: + return STR_WIFI_NOT_CONNECTED_ZH; + case StringKey::WiFiConnected: + return STR_WIFI_CONNECTED_ZH; + case StringKey::SSID: + return STR_SSID_ZH; + case StringKey::LoRaInfo: + return STR_LORA_INFO_ZH; + case StringKey::LoRa: + return STR_LORA_ZH; + case StringKey::Role: + return STR_ROLE_ZH; + case StringKey::Freq: + return STR_FREQ_ZH; + case StringKey::ChUtil: + return STR_CHUTIL_ZH; + case StringKey::System: + return STR_SYSTEM_ZH; + case StringKey::Heap: + return STR_HEAP_ZH; + case StringKey::PSRAM: + return STR_PSRAM_ZH; + case StringKey::Flash: + return STR_FLASH_ZH; + case StringKey::SD: + return STR_SD_ZH; + case StringKey::Ver: + return STR_VER_ZH; + case StringKey::Up: + return STR_UP_ZH; + case StringKey::ClientApp: + return STR_CLIENT_APP_ZH; + case StringKey::NoConnected: + return STR_NO_CONNECTED_ZH; + case StringKey::LOCKED: + return STR_LOCKED_ZH; + case StringKey::ConnectToUnlock: + return STR_CONNECT_TO_UNLOCK_ZH; + case StringKey::Battery: + return STR_BATTERY_ZH; + case StringKey::Resuming: + return STR_RESUMING_ZH; + case StringKey::LastHeard: + return STR_LAST_HEARD_ZH; + case StringKey::HopsSig: + return STR_HOPS_SIG_ZH; + case StringKey::HopsSignal: + return STR_HOPS_SIGNAL_ZH; + case StringKey::Nodes: + return STR_NODES_ZH; + case StringKey::Distance: + return STR_DISTANCE_ZH; + case StringKey::Bearings: + return STR_BEARINGS_ZH; + case StringKey::CreatingSSLCert: + return STR_CREATING_SSL_ZH; + case StringKey::PleaseWait: + return STR_PLEASE_WAIT_ZH; + case StringKey::CriticalFault: + return STR_CRITICAL_FAULT_ZH; + case StringKey::ForHelpVisit: + return STR_FOR_HELP_VISIT_ZH; + case StringKey::Updating: + return STR_UPDATING_ZH; + case StringKey::PleaseBePatient: + return STR_PLEASE_BE_PATIENT_ZH; + case StringKey::RebootInProgress: + return STR_REBOOT_IN_PROGRESS_ZH; + case StringKey::PowerMenu: + return STR_POWER_MENU_ZH; + case StringKey::SystemMenu: + return STR_SYSTEM_MENU_ZH; + case StringKey::ScreenOptions: + return STR_SCREEN_OPTIONS_ZH; + case StringKey::WifiToggle: + return STR_WIFI_TOGGLE_ZH; + case StringKey::BluetoothToggle: + return STR_BLUETOOTH_TOGGLE_ZH; + case StringKey::NodeDbReset: + return STR_NODE_DB_RESET_ZH; + case StringKey::ManageNode: + return STR_MANAGE_NODE_ZH; + case StringKey::RemoveFavorite: + return STR_REMOVE_FAVORITE_ZH; + case StringKey::AddFavorite: + return STR_ADD_FAVORITE_ZH; + case StringKey::TestMenu: + return STR_TEST_MENU_ZH; + case StringKey::TraceRoute: + return STR_TRACE_ROUTE_ZH; + case StringKey::ThrottleMessage: + return STR_THROTTLE_MSG_ZH; + case StringKey::MessageViewMode: + return STR_MSG_VIEW_MODE_ZH; + case StringKey::DeleteMessages: + return STR_DELETE_MESSAGES_ZH; + case StringKey::NodeNameLength: + return STR_NODE_NAME_LENGTH_ZH; + case StringKey::GPSMenu: + return STR_GPS_MENU_ZH; + case StringKey::GPSToggle: + return STR_GPS_TOGGLE_ZH; + case StringKey::GPSFormat: + return STR_GPS_FORMAT_ZH; + case StringKey::GPSSmartPosition: + return STR_GPS_SMART_POS_ZH; + case StringKey::GPSUpdateInterval: + return STR_GPS_UPDATE_INTERVAL_ZH; + case StringKey::GPSPositionBroadcast: + return STR_GPS_POS_BCAST_ZH; + case StringKey::CompassNorth: + return STR_COMPASS_NORTH_ZH; + case StringKey::BuzzerMode: + return STR_BUZZER_MODE_ZH; + case StringKey::HamModeConfirm: + return STR_HAM_MODE_ZH; + case StringKey::LicensedToNormal: + return STR_LICENSED_NORMAL_ZH; + case StringKey::KeyVerification: + return STR_KEY_VERIFY_ZH; + case StringKey::RegionUnset: + return STR_REGION_UNSET_ZH; + case StringKey::SelectRegionMessage: + return STR_SELECT_REGION_ZH; + case StringKey::Rebooting: + return STR_REBOOTING_ZH; + default: + return "?"; + } +} + +} // namespace l10n +} // namespace meshtastic