#include "I18n.h" #include #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 = 2; I18n& I18n::getInstance() { static I18n instance; return instance; } const char* I18n::get(StrId id) const { const auto index = static_cast(id); if (index >= static_cast(StrId::_COUNT)) { return "???"; } // Use generated helper function - no hardcoded switch needed! const LangStrings lang = getLanguageStrings(_language); const char* result = lang.data + lang.offsets[index]; if (_language != Language::EN && result[0] == '\0') { const LangStrings english = getLanguageStrings(Language::EN); return english.data + english.offsets[index]; } return result; } void I18n::setLanguage(Language lang) { if (lang >= Language::_COUNT) { return; } _language = lang; saveSettings(); } const char* I18n::getLanguageName(Language lang) const { const auto index = static_cast(lang); if (index >= static_cast(Language::_COUNT)) { return "???"; } 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"); FsFile file; if (!Storage.openFileForWrite("I18N", SETTINGS_FILE, file)) { LOG_ERR("I18N", "Failed to save settings"); return; } serialization::writePod(file, SETTINGS_VERSION); serialization::writeString(file, getLanguageCode(_language)); file.close(); LOG_INF("I18N", "Settings saved: language=%d code=%s", static_cast(_language), getLanguageCode(_language)); } void I18n::loadSettings() { FsFile file; if (!Storage.openFileForRead("I18N", SETTINGS_FILE, file)) { LOG_INF("I18N", "No settings file, using default (English)"); return; } uint8_t version; serialization::readPod(file, version); 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) { LOG_INF("I18N", "Loaded language code: %s (%d)", code.c_str(), static_cast(_language)); } else { LOG_ERR("I18N", "Unknown language code in settings: %s", code.c_str()); } file.close(); return; } // 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); LOG_INF("I18N", "Migrating v1 language index: %d -> %s", static_cast(_language), getLanguageCode(_language)); file.close(); saveSettings(); return; } file.close(); LOG_ERR("I18N", "Invalid v1 language index: %d", static_cast(lang)); return; } LOG_ERR("I18N", "Settings version mismatch: %d\n", static_cast(version)); file.close(); } // Generate character set for a specific language const char* I18n::getCharacterSet(Language lang) { const auto langIndex = static_cast(lang); if (langIndex >= static_cast(Language::_COUNT)) { lang = Language::EN; // Fallback to first language } return CHARACTER_SETS[static_cast(lang)]; }