Working with Translations
Section titled “Working with Translations”Relevant source files
- docs/architecture/gcode_host_streaming_flow.md
- docs/roadmap/grbl_no_sd_preparation.md
- docs/ux_flows/grblhal/change_tool_screen.md
- docs/ux_flows/grblhal/files_screen.md
- docs/ux_flows/grblhal/settings_list_screen.md
- main/core/commands/esp460.cpp
- main/core/esp3d_hal.cpp
- main/core/includes/esp3d_hal.h
- main/display/cnc/fluidnc/screens/change_tool_screen.h
- main/display/cnc/fluidnc/screens/esp3d_screen_type.cpp
- main/display/cnc/fluidnc/screens/esp3d_screen_type.h
- main/display/cnc/fluidnc/screens/files_screen.cpp
- main/display/cnc/grblhal/screens/esp3d_screen_type.cpp
- main/display/esp3d_translations_init.cpp
- main/display/esp3d_translations_list.h
- main/modules/translations/esp3d_translation_service.cpp
- main/modules/translations/esp3d_translation_service.h
- tools/language_packs/template.lng
- tools/language_packs/ui_fr-fr.lng
Purpose and Scope
Section titled “Purpose and Scope”This page explains the internationalization (i18n) system used throughout the PiBot CNC Pendant firmware. It details how to add new translatable strings using the X-macro pattern, the structure of language files, the architecture of the ESP3DTranslationService, and the process for runtime language switching.
The system is designed for high memory efficiency, using memory-mapped LNG1 blobs for active translations and a flash-resident English fallback table, ensuring zero DRAM usage for string storage.
Translation System Architecture
Section titled “Translation System Architecture”The translation system uses a triple-expansion X-macro pattern. A single definition of a translation string is used to generate the ESP3DLabel enumeration, a lookup map for file-based IDs, and the default English string values stored in flash .rodata.
Translation Data Flow
Section titled “Translation Data Flow”Sources:main/display/esp3d_translations_list.h#36-47main/display/esp3d_translations_init.cpp#26-37main/modules/translations/esp3d_translation_service.cpp#132-139
Implementation Details
Section titled “Implementation Details”The X-Macro Pattern
Section titled “The X-Macro Pattern”The system relies on the ESP3D_TR_DEF(label, id, text) macro. It is expanded in different contexts:
- Enum Generation: In
esp3d_translations_list.h, it creates theESP3DLabelenum class used in C++ code. main/display/esp3d_translations_list.h#37-47 - English Fallback Table: In
esp3d_translations_init.cpp, it populateskEnglishTexts[], aconst char* constarray stored in flash. main/display/esp3d_translations_init.cpp#31-37 - ID Mapping: In
esp3d_translations_id_map.h, it creates a mapping between the enum and the stablefile_id(represented asl_X) used in language packs. main/modules/translations/esp3d_translation_service.cpp#132-139
Fixed ID Ranges
Section titled “Fixed ID Ranges”To prevent conflicts between core UI elements and specific CNC firmware features, IDs are partitioned:
- 0-499: Core UI labels (Settings, Files, Info). tools/language_packs/template.lng#7
- 500-999: CNC System labels (Jogging, Probing, Tool Change). tools/language_packs/template.lng#8
- 1000+: Target-specific labels (FluidNC/grblHAL unique features). tools/language_packs/template.lng#9
Sources:main/display/esp3d_translations_init.cpp#31-37tools/language_packs/template.lng#6-9
Working with Language Files
Section titled “Working with Language Files”Language files exist in two forms: raw .lng text files for development and binary LNG1 blobs stored in the ui_resources partition for production.
File Format (.lng)
Section titled “File Format (.lng)”The source files use an INI-style format with [translations] and [info] sections. tools/language_packs/template.lng#11-196
[translations]l_0=Englishl_1=Settingsl_500=Change Tool
[info]code=enname=English (default)- l_X: The key represents the fixed ID defined in the
.incfiles. IDs are permanent and must never be changed once assigned. tools/language_packs/template.lng#3 - l_0: By convention, ID 0 is the name of the language itself. tools/language_packs/template.lng#14
Sources:tools/language_packs/template.lng#3-14tools/language_packs/ui_fr-fr.lng#1-77
Binary Blob Layout (LNG1)
Section titled “Binary Blob Layout (LNG1)”For runtime performance, translations are compiled into binary blobs via lang_packs.py. The layout consists of:
- Header: 40-byte
LangBlobHeaderincluding magic'LNG1', language code, and entry count. main/modules/translations/esp3d_translation_service.cpp#51-57 - Index: Sorted array of
LangBlobEntrycontaining thefile_idand string offset. main/modules/translations/esp3d_translation_service.cpp#58-61 - Pool: NUL-terminated UTF-8 strings. main/modules/translations/esp3d_translation_service.cpp#48
Sources:main/modules/translations/esp3d_translation_service.cpp#45-65
Using Translations in Code
Section titled “Using Translations in Code”Accessing Strings
Section titled “Accessing Strings”The primary interface is esp3dTranslationService.translate(ESP3DLabel label, ...). It supports printf-style formatting and returns a pointer directly to the mmapped flash or .rodata when no formatting is required (zero-copy). main/modules/translations/esp3d_translation_service.h#64-67
// Example: Setting a label text with translationlv_label_set_text(status_label, esp3dTranslationService.translate(ESP3DLabel::loading));For applications requiring raw strings (e.g., the [ESP460]DUMP command), getRawTranslation() is used to preserve format specifiers. main/modules/translations/esp3d_translation_service.h#71main/core/commands/esp460.cpp#61
Language Management
Section titled “Language Management”The ESP3DTranslationService singleton manages the lifecycle of translation data:
- Scanning:
getLanguagesList()scans partition slots (identified byESP3D_LANG_SLOT_IDS_INIT) for availableLNG1blobs. main/modules/translations/esp3d_translation_service.h#81main/modules/translations/esp3d_translation_service.cpp#67-68 - Loading:
begin()reads the language setting (ESP3DSettingIndex::esp3d_ui_language) and maps the corresponding flash partition blob. main/modules/translations/esp3d_translation_service.cpp#180-205 - Lookup:
lookup(label)first checks the active binary blob using a binary search; if the label is missing or no blob is loaded, it falls back toesp3d_translations_english(label). main/modules/translations/esp3d_translation_service.cpp#278-305
Sources:main/modules/translations/esp3d_translation_service.cpp#180-205main/modules/translations/esp3d_translation_service.h#64-71
Adding New Translatable Strings
Section titled “Adding New Translatable Strings”To add a new string to the firmware:
- Identify Category: Choose Core (0-499), System (500-999), or Target (1000+).
- Edit .inc File: Add a new
ESP3D_TR_DEFat the end of the appropriate section.
- Example:
ESP3D_TR_DEF(my_new_status, 550, "Status Active")
- Use in UI: Reference the new enum member:
ESP3DLabel::my_new_status. - Update Translations: Add
l_550=Status Actifto.lngfiles.
Implementation Flow for New String
Section titled “Implementation Flow for New String”Sources:main/display/esp3d_translations_list.h#37-47main/modules/translations/esp3d_translation_service.cpp#132-139main/modules/translations/esp3d_translation_service.cpp#278-305
Testing and Debugging
Section titled “Testing and Debugging”- Validation: The service validates blobs during initialization, checking the “LNG1” magic header, entry sorting, and string termination. main/modules/translations/esp3d_translation_service.cpp#83-120
- Missing Labels: If a label index exceeds the internal tables, the fallback mechanism returns
???. main/display/esp3d_translations_init.cpp#47-49 - Dumping: The
ESP460command enables dumping the current translation table for verification. main/core/commands/esp460.cpp#51-77 - Language Selection: The
languages_screen(implemented viaListMenuScreen) allows runtime switching by scanning the filesystem/partitions for available packs. main/display/cnc/fluidnc/screens/esp3d_screen_type.cpp#134-136
Sources:main/modules/translations/esp3d_translation_service.cpp#83-120main/display/esp3d_translations_init.cpp#45-51main/core/commands/esp460.cpp#51-77main/display/cnc/fluidnc/screens/esp3d_screen_type.cpp#134-136