## Summary * **What is the goal of this PR?** (e.g., Implements the new feature for file uploading.) Currently we are displaying the languages in the order they were added (as in the `Language` enum). However, as new languages are coming in, this will quickly be confusing to the users. But we can't just change the ordering of the enum if we want to respect bakwards compatibility. So my proposal is to add a mapping of the alphabetical order of the languages. I've made it so that it's generated by the `gen_i18n.py` script, which will be used when a new language is added. * **What changes are included?** Added the array from the python script and changed `LanguageSelectActivity` to use the indices from there. Also commited the generated `I18nKeys.h` ## Additional Context * Add any other information that might be helpful for the reviewer (e.g., performance implications, potential risks, specific areas to focus on). I was wondering if there is a better way to sort it. Currently, it's by unicode value and Czech and Russian are last, which I don't know it it's the most intuitive. The current order is: `Català, Deutsch, English, Español, Français, Português (Brasil), Română, Svenska, Čeština, Русский` --- ### 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 >**_
5.7 KiB
Internationalization (I18N)
This guide explains the multi-language support system in CrossPoint Reader.
Supported Languages
- English
- French
- German
- Portuguese
- Spanish
- Swedish
- Czech
- Russian
- Ukrainian
For Developers
Translation System Architecture
The I18N system uses per-language YAML files to maintain translations and a Python script to generate C++ code:
lib/I18n/
├── translations/ # One YAML file per language
│ ├── english.yaml
│ ├── spanish.yaml
│ ├── french.yaml
│ └── ...
├── I18n.h
├── I18n.cpp
├── I18nKeys.h # Enums (auto-generated)
├── I18nStrings.h # String array declarations (auto-generated)
└── I18nStrings.cpp # String array definitions (auto-generated)
scripts/
└── gen_i18n.py # Code generator script
Key principle: All translations are managed in the YAML files under lib/I18n/translations/. The Python script generates the necessary C++ code automatically.
YAML File Format
Each language has its own file in lib/I18n/translations/ (e.g. spanish.yaml).
A file looks like this:
_language_name: "Español"
_language_code: "ES"
_order: "1"
STR_CROSSPOINT: "CrossPoint"
STR_BOOTING: "BOOTING"
STR_BROWSE_FILES: "Buscar archivos"
Metadata keys (prefixed with _):
_language_name— Native display name shown to the user (e.g. "Français")_language_code— C++ enum name (e.g. "FR"). Please use the ISO Code of the language. Must be a valid C++ identifier._order— Controls the position in the Language enum (English is always 0)
Rules:
- Use UTF-8 encoding
- Every line must follow the format:
KEY: "value" - Keys must be valid C++ identifiers (uppercase, strats with STR_)
- Keys must be unique within a file
- String values must be quoted
- Use
\nfor newlines,\\for literal backslashes,\"for literal quotes inside values
Adding New Strings
To add a new translatable string:
1. Edit the English YAML file
Add the key to lib/I18n/translations/english.yaml:
STR_MY_NEW_STRING: "My New String"
Then add translations in each language file. If a key is missing from a language file, the generator will automatically use the English text as a fallback (and print a warning).
2. Run the generator script
python3 scripts/gen_i18n.py lib/I18n/translations lib/I18n/
This automatically:
- Fills missing translations from English
- Updates the
StrIdenum inI18nKeys.h - Regenerates all language arrays in
I18nStrings.cpp
3. Use in code
#include <I18n.h>
// Using the tr() macro (recommended)
renderer.drawText(font, x, y, tr(STR_MY_NEW_STRING));
// Using I18N.get() directly
const char* text = I18N.get(StrId::STR_MY_NEW_STRING);
That's it! No manual array synchronization needed.
Adding a New Language
To add support for a new language (e.g., Italian):
1. Create a new YAML file
Create lib/I18n/translations/italian.yaml:
_language_name: "Italiano"
_language_code: "IT"
_order: "7"
STR_CROSSPOINT: "CrossPoint"
STR_BOOTING: "AVVIO"
You only need to include the strings you have translations for. Missing keys will fall back to English automatically.
2. Run the generator
python3 scripts/gen_i18n.py lib/I18n/translations lib/I18n/
This automatically updates all necessary code.
Modifying Existing Translations
Simply edit the relevant YAML file and regenerate:
python3 scripts/gen_i18n.py lib/I18n/translations lib/I18n/
UTF-8 Encoding
The YAML files use UTF-8 encoding. Special characters are automatically converted to C++ UTF-8 hex sequences by the generator.
I18N API Reference
// === Convenience Macros (Recommended) ===
// tr(id) - Get translated string without StrId:: prefix
const char* text = tr(STR_SETTINGS_TITLE);
renderer.drawText(font, x, y, tr(STR_BROWSE_FILES));
Serial.printf("Status: %s\n", tr(STR_CONNECTED));
// I18N - Shorthand for I18n::getInstance()
I18N.setLanguage(Language::ES);
Language lang = I18N.getLanguage();
// === Full API ===
// Get the singleton instance
I18n& instance = I18n::getInstance();
// Get translated string (three equivalent ways)
const char* text = tr(STR_SETTINGS_TITLE); // Macro (recommended)
const char* text = I18N.get(StrId::STR_SETTINGS_TITLE); // Direct call
const char* text = I18N[StrId::STR_SETTINGS_TITLE]; // Operator overload
// Set language
I18N.setLanguage(Language::ES);
// Get current language
Language lang = I18N.getLanguage();
// Save language setting to file
I18N.saveSettings();
// Load language setting from file
I18N.loadSettings();
// Get character set for font subsetting (static method)
const char* chars = I18n::getCharacterSet(Language::FR);
File Storage
Language settings are stored in:
/.crosspoint/language.bin
This file contains:
- Version byte
- Current language selection (1 byte)
Translation Workflow
For Developers (Adding Features)
- Add new strings to
lib/I18n/translations/english.yaml - Run
python3 scripts/gen_i18n.py lib/I18n/translations lib/I18n/ - Use the new
StrIdin your code - Request translations from translators
For Translators
- Open the YAML file for your language in
lib/I18n/translations/ - Add or update translations using the format
STR_KEY: "translated text" - Keep translations concise (E-ink space constraints)
- Make sure the file is in UTF-8 encoding
- Run
python3 scripts/gen_i18n.py lib/I18n/translations lib/I18n/to verify - Test on device or submit for review