Internationalization
CrossInk keeps user-facing UI text in per-language YAML files and generates the C++ lookup tables used by the firmware.
English is the source of truth. Add new STR_* keys to lib/I18n/translations/english.yaml, then propagate translations to the other YAML files when possible. If a non-English file is missing a key, the generator falls back to the English text for that language.
Supported Languages
The current translation files are:
- English (
EN) - Español (
ES) - Français (
FR) - Deutsch (
DE) - Čeština (
CS) - Português (Brasil) (
PT) - Русский (
RU) - Svenska (
SV) - Română (
RO) - Català (
CA) - Українська (
UK) - Беларуская (
BE) - Italiano (
IT) - Polski (
PL) - Suomi (
FI) - Dansk (
DA) - Nederlands (
NL) - Türkçe (
TR) - Қазақша (
KK) - Magyar (
HU) - Lietuvių (
LT) - Slovenščina (
SI) - Valencià (
CAV) - עברית (
HE) - Tiếng Việt (
VI)
Files
Translation sources live in:
lib/I18n/translations/*.yaml
Generated firmware files live in:
lib/I18n/I18nKeys.h
lib/I18n/I18nStrings.h
lib/I18n/I18nStrings.cpp
The generator is:
scripts/gen_i18n.py
Do not edit the generated files directly. Edit the YAML files and rerun the generator.
YAML Format
Each language file is a flat KEY: "value" mapping.
_language_name: "Deutsch"
_language_code: "DE"
_order: "3"
STR_CROSSPOINT: "CrossInk"
STR_BOOTING: "BOOTING"
STR_BROWSE_FILES: "Dateien durchsuchen"
Metadata keys:
_language_nameis the display name shown to users._language_codeis the generatedLanguageenum value and must be a valid C++ identifier._ordercontrols enum order. English is always first, and every_ordervalue must be unique.
Rules:
- Use UTF-8.
- Keep the file as plain
KEY: "value"lines. Do not add comments. - Use
STR_*keys for translatable strings. - Quote every value.
- Use
\nfor newlines,\\for a literal backslash, and\"for a quote inside a value. - Keep UI text concise. Many surfaces are small on the e-ink display.
Adding Or Changing Strings
- Add or update the key in
lib/I18n/translations/english.yaml. - Add or update the same key in every non-English YAML file when you have a translation.
- Regenerate the C++ files:
python3 scripts/gen_i18n.py lib/I18n/translations lib/I18n
- Build the simulator or firmware to make sure the generated files compile:
pio run -e simulator
For a firmware-target check:
pio run -e default
The generator prints fallback information for missing translations and warns about extra keys that exist outside English.
Adding A Language
- Create a new file in
lib/I18n/translations/, for exampleexample.yaml. - Add
_language_name,_language_code, and a unique_order. - Add any translated
STR_*keys you already have. - Run the generator.
- Build with PlatformIO.
Missing strings will fall back to English, but a new language should not ship with large English-only areas unless that is intentional.
Generated Runtime Model
scripts/gen_i18n.py reads the YAML files and generates:
Language,StrId, language-code arrays, display-name arrays, and migration helpers inI18nKeys.h- Flat UTF-8 string blobs and offset tables in
I18nStrings.cpp - Character sets for each language, used by font tooling
The flat blob plus offset-table layout keeps lookup simple and avoids a large table of individual C string pointers. When a translation falls back to English, the generated offset uses the English string data instead of duplicating the text.
In firmware code, use tr(STR_KEY) for user-facing strings:
renderer.drawText(font, x, y, tr(STR_SETTINGS_TITLE));
Direct API access is also available:
const char* text = I18N.get(StrId::STR_SETTINGS_TITLE);
const char* name = I18N.getLanguageName(Language::DE);
Language lang = I18n::languageFromCode("DE");
const char* charset = I18n::getCharacterSet(Language::DE);
Persisted Language Setting
The selected language is stored with the rest of the device settings in:
/.crosspoint/crossink-settings.json
The language field is stored as a stable language code string such as "EN", "DE", or "HE", not as a raw enum number.
Current firmware falls back to /.crosspoint/settings.json when the namespaced CrossInk settings file is not present, then saves the loaded settings to crossink-settings.json.
Older firmware used:
/.crosspoint/language.bin
Current firmware migrates that legacy file into crossink-settings.json and renames the legacy file to language.bin.bak.
Validation Checklist
Before submitting translation changes:
- Run the generator:
python3 scripts/gen_i18n.py lib/I18n/translations lib/I18n
- Check for accidental fallback English values where a real translation was expected:
rg -n '^STR_MY_KEY:' lib/I18n/translations
- Build at least the simulator:
pio run -e simulator
- If the changed text appears in a cramped UI surface, check it on device or in the simulator for clipping.
Notes For Translators
- Keep translations short where possible.
- Preserve placeholders, punctuation, and units from the English string unless the target language needs different grammar.
- Do not translate internal names that are meant to stay as product or file-system labels.
- If a full translation would be too long for a button hint or compact label, ask for a dedicated short
STR_*key instead of shortening the full title globally.