# Internationalization (I18N) This guide explains the multi-language support system in CrossPoint Reader. ## Supported Languages - English - French - German - Portuguese - Spanish - Swedish - Czech - Russian --- ## 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: ```yaml _language_name: "Español" _language_code: "SPANISH" _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. "FRENCH"). 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 `\n` for 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`: ```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 ```bash python3 scripts/gen_i18n.py lib/I18n/translations lib/I18n/ ``` This automatically: - Fills missing translations from English - Updates the `StrId` enum in `I18nKeys.h` - Regenerates all language arrays in `I18nStrings.cpp` #### 3. Use in code ```cpp #include // 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`: ```yaml _language_name: "Italiano" _language_code: "ITALIAN" _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 ```bash 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: ```bash 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 ```cpp // === 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::SPANISH); 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::SPANISH); // 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::FRENCH); ``` --- ## 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) 1. Add new strings to `lib/I18n/translations/english.yaml` 2. Run `python3 scripts/gen_i18n.py lib/I18n/translations lib/I18n/` 3. Use the new `StrId` in your code 4. Request translations from translators ### For Translators 1. Open the YAML file for your language in `lib/I18n/translations/` 2. Add or update translations using the format `STR_KEY: "translated text"` 3. Keep translations concise (E-ink space constraints) 4. Make sure the file is in UTF-8 encoding 5. Run `python3 scripts/gen_i18n.py lib/I18n/translations lib/I18n/` to verify 6. Test on device or submit for review