refactor: Move language setting into JSON settings (#1796)

## Summary

Another follow up to #1408. That PR revised the language.bin settings
file to version 2. After merging, it occurred to me that it would make
more sense to have the language inside the overall settings.json file,
easy for users to see and change directly.

This change adds migration from language.bin to settings.json. It only
migrates from the pre-#1408 v1 language.bin format, because v2 has only
been in for a couple of commits and I don't think we need long-term
maintenance code to handle it.

#1408 had a potential long-term maintenance issue for migration. It
assumed that the enum order of languages never changes, only grows. So,
e.g. "Portuguese (Portugal)" could never be added next to "Portuguese
(Brazil)", only appended. This change includes a hardcoded frozen
ordering of the language indices as they existed at 2f969a9, and
migrates to storing a language code string in the settings.json instead
for future reordering flexibility.

Similar to the settings.bin to settings.json migration, which renames
settings.bin to settings.bin.bak, this change renames language.bin to
language.bin.bak after adding the language setting to settings.json.

---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**PARTIALLY**_
This commit is contained in:
Zach Nelson
2026-05-01 09:39:28 -05:00
committed by GitHub
parent ae865f6d08
commit b25389b43e
8 changed files with 75 additions and 77 deletions
+6 -69
View File
@@ -1,19 +1,12 @@
#include "I18n.h"
#include <HalStorage.h>
#include <Logging.h>
#include <Serialization.h>
#include <string>
#include <cstddef>
#include <cstring>
#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;
@@ -35,7 +28,6 @@ void I18n::setLanguage(Language lang) {
return;
}
_language = lang;
saveSettings();
}
const char* I18n::getLanguageName(Language lang) const {
@@ -46,66 +38,11 @@ const char* I18n::getLanguageName(Language lang) const {
return LANGUAGE_NAMES[index];
}
const char* I18n::getLanguageCode(Language lang) const {
const auto index = static_cast<size_t>(lang);
if (index >= static_cast<size_t>(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);
const char* code = getLanguageCode(_language);
serialization::writeString(file, code);
LOG_DBG("I18N", "Settings saved: code=%s", code);
}
void I18n::loadSettings() {
FsFile file;
if (!Storage.openFileForRead("I18N", SETTINGS_FILE, file)) {
LOG_DBG("I18N", "No settings file, using default");
return;
}
uint8_t version;
serialization::readPod(file, version);
if (version == SETTINGS_VERSION) {
std::string code;
serialization::readString(file, code);
for (uint8_t i = 0; i < getLanguageCount(); i++) {
if (code == LANGUAGE_CODES[i]) {
_language = static_cast<Language>(i);
LOG_DBG("I18N", "Loaded language: %s", code.c_str());
return;
}
}
LOG_ERR("I18N", "Unknown language code: %s", code.c_str());
return;
}
if (version == 1) {
uint8_t lang;
serialization::readPod(file, lang);
if (lang < static_cast<size_t>(Language::_COUNT)) {
_language = static_cast<Language>(lang);
saveSettings();
LOG_INF("I18N", "Migrated v1 language setting");
}
Language I18n::languageFromCode(const char* code) {
for (uint8_t i = 0; i < getLanguageCount(); i++) {
if (strcmp(code, LANGUAGE_CODES[i]) == 0) return static_cast<Language>(i);
}
return Language::EN;
}
// Generate character set for a specific language
+1 -4
View File
@@ -22,11 +22,8 @@ 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();
void loadSettings();
static Language languageFromCode(const char* code);
// Get all unique characters used in a specific language
// Returns a sorted string of unique characters