diff --git a/lib/I18n/I18n.cpp b/lib/I18n/I18n.cpp index c77fce9a..9ccb3cd4 100644 --- a/lib/I18n/I18n.cpp +++ b/lib/I18n/I18n.cpp @@ -4,13 +4,15 @@ #include #include +#include + #include "I18nStrings.h" using namespace i18n_strings; // Settings file path static constexpr const char* SETTINGS_FILE = "/.crosspoint/language.bin"; -static constexpr uint8_t SETTINGS_VERSION = 1; +static constexpr uint8_t SETTINGS_VERSION = 2; I18n& I18n::getInstance() { static I18n instance; @@ -44,6 +46,14 @@ const char* I18n::getLanguageName(Language lang) const { return LANGUAGE_NAMES[index]; } +const char* I18n::getLanguageCode(Language lang) const { + const auto index = static_cast(lang); + if (index >= static_cast(Language::_COUNT)) { + return LANGUAGE_CODES[0]; + } + return LANGUAGE_CODES[index]; +} + void I18n::saveSettings() { Storage.mkdir("/.crosspoint"); @@ -54,10 +64,11 @@ void I18n::saveSettings() { } serialization::writePod(file, SETTINGS_VERSION); - serialization::writePod(file, static_cast(_language)); + serialization::writeString(file, getLanguageCode(_language)); file.close(); - Serial.printf("[I18N] Settings saved: language=%d\n", static_cast(_language)); + Serial.printf("[I18N] Settings saved: language=%d code=%s\n", static_cast(_language), + getLanguageCode(_language)); } void I18n::loadSettings() { @@ -69,19 +80,48 @@ void I18n::loadSettings() { uint8_t version; serialization::readPod(file, version); - if (version != SETTINGS_VERSION) { - Serial.printf("[I18N] Settings version mismatch\n"); + + if (version == SETTINGS_VERSION) { + std::string code; + serialization::readString(file, code); + bool found = false; + + for (uint8_t i = 0; i < getLanguageCount(); i++) { + if (code == LANGUAGE_CODES[i]) { + _language = static_cast(i); + found = true; + break; + } + } + + if (found) { + Serial.printf("[I18N] Loaded language code: %s (%d)\n", code.c_str(), static_cast(_language)); + } else { + Serial.printf("[I18N] Unknown language code in settings: %s\n", code.c_str()); + } file.close(); return; } - uint8_t lang; - serialization::readPod(file, lang); - if (lang < static_cast(Language::_COUNT)) { - _language = static_cast(lang); - Serial.printf("[I18N] Loaded language: %d\n", static_cast(_language)); + // Legacy migration path: version 1 stored language enum index directly. + if (version == 1) { + uint8_t lang; + serialization::readPod(file, lang); + if (lang < static_cast(Language::_COUNT)) { + _language = static_cast(lang); + Serial.printf("[I18N] Migrating v1 language index: %d -> %s\n", static_cast(_language), + getLanguageCode(_language)); + file.close(); + saveSettings(); + return; + } + file.close(); + Serial.printf("[I18N] Invalid v1 language index: %d\n", static_cast(lang)); + return; } + Serial.printf("[I18N] Settings version mismatch: %d\n", static_cast(version)); + file.close(); } diff --git a/lib/I18n/I18n.h b/lib/I18n/I18n.h index 347b7fcc..74273b30 100644 --- a/lib/I18n/I18n.h +++ b/lib/I18n/I18n.h @@ -22,6 +22,7 @@ class I18n { Language getLanguage() const { return _language; } void setLanguage(Language lang); + const char* getLanguageCode(Language lang) const; const char* getLanguageName(Language lang) const; void saveSettings(); diff --git a/scripts/gen_i18n.py b/scripts/gen_i18n.py index bebfef9f..520be52f 100755 --- a/scripts/gen_i18n.py +++ b/scripts/gen_i18n.py @@ -471,6 +471,10 @@ def generate_keys_header( lines.append("") # Extern declarations + lines.append("// Language codes (defined in I18nStrings.cpp)") + lines.append("extern const char* const LANGUAGE_CODES[];") + lines.append("") + lines.append("// Language display names (defined in I18nStrings.cpp)") lines.append("extern const char* const LANGUAGE_NAMES[];") lines.append("") @@ -584,14 +588,22 @@ def generate_strings_cpp( ) -> None: """Generate I18nStrings.cpp.""" lines: List[str] = [ - '#include "I18nStrings.h"', - "#include ", - "", "// THIS FILE IS AUTO-GENERATED BY gen_i18n.py. DO NOT EDIT.", "// clang-format off", + '#include "I18nStrings.h"', + "", + "#include ", "", ] + # LANGUAGE_NAMES array + lines.append("// Language codes") + lines.append("const char* const LANGUAGE_CODES[] = {") + for code in languages: + _append_string_entry(lines, code) + lines.append("};") + lines.append("") + # LANGUAGE_NAMES array lines.append("// Language display names") lines.append("const char* const LANGUAGE_NAMES[] = {")