Files
crosspoint-reader-mod/docs/i18n.md
Mirus 4ccafe5cfa feat: add Ukrainian translation (#1065)
## Summary

* **What is the goal of this PR?**
A Ukrainian translation for the GUI

* **What changes are included?**
Everything according to
https://github.com/crosspoint-reader/crosspoint-reader/blob/master/docs/i18n.md

## Additional Context

* Add any other information that might be helpful for the reviewer
(e.g., performance implications, potential risks,
  specific areas to focus on).

Nope

---

### 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 >**_ as a
consistency validation
2026-02-22 20:34:44 +03:00

5.6 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: "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:

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 StrId enum in I18nKeys.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: "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

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::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