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

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

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