Documentation / Designing

Translations

One design, several languages, switched on the device - and told before you flash it which words will not fit and which letters the font cannot draw.

A design starts in one language: the text you typed on its widgets. Add a second on the Languages tab and that text becomes the base language, each text you mark gets a box for every other language, and the export carries all of them. A Set language event on a button switches between them on the panel.

Adding a language and marking text for translation are part of Pro. A design that already has translations opens, edits, previews and exports for anybody - opening one in a free account loses nothing.

Languages

The Languages tab is in the bottom panel, beside Fonts and Icons. Add a language takes a name and a short id; pick a common one from the list and both are filled in.

A design holds up to 32 languages. Each is a complete table of text in the firmware, so the cost is the text itself - a few kilobytes for a typical design, not a copy of the UI.

Marking text

Nothing is translated until you say so. Five kinds of text can be, each through a To be translated box in the widget's settings:

WidgetWhat is translated
LabelIts text
ButtonIts caption. An icon on the button stays as it is.
CheckboxThe text beside the box
Text areaThe placeholder. What somebody has typed into it is left alone.
DropdownThe options, one to a line. Keep the same number of lines in every language: the selection is a position in the list, and it is kept when the language changes.

Mark all on the Languages tab ticks every one of them in the design at once, which is the usual first step on a design drawn before anybody thought of a second language. Leave unmarked what should not change: a unit, a product name, a number.

A text a binding writes is not yours to translate. A label bound to a variable is rewritten on every refresh, which would replace whatever the language set. The Problems tab says so; put the translated caption in a label of its own beside the value.

The table

With two languages and one marked text, the tab becomes a table: a row for each text, a column for each language. The base column is the text on the widget, read only - change it on the widget. Click the name at the start of a row to select that widget on the canvas.

Translations follow their widget. A duplicated or pasted widget takes its translations with it, and deleting a widget drops them.

CSV: sending it to a translator

Export CSV writes one row for each marked text and one column for each language:

key,where,en,de,fr
w_k3f91a:text,Home / Title,Settings,Einstellungen,
w_k3fa2c:options,Home / Units,"Celsius
Fahrenheit","Celsius
Fahrenheit",

key is what a row is matched on when the file comes back - it does not change when a widget is renamed or moved, so leave it alone. where is the screen and the widget, for the person translating, and is never read. The file is UTF-8 with a byte-order mark, which is what makes Excel open it with its accents intact.

Import CSV reads the file back and says exactly what it did:

A file saved with semicolons or tabs is read as it is - a spreadsheet in a locale that writes 1,5 saves "CSV" that way. A file that is not UTF-8 is refused whole, with how to save it properly, because its accented letters are already lost. One import is one undo step.

Seeing a language

Once a design has a second language, a selector with a globe appears in the canvas toolbar. Choosing German draws every screen in German: on the canvas, in preview and in the LCD view. It is a way of looking - nothing in the design changes and nothing is added to undo. A text with no translation shows in the base language, exactly as on the device.

In preview, a Set language event really switches, and the selector follows it.

What is checked

A translated screen fails quietly, and each way it can is on the Problems tab with the widget and the language named and a link that selects the widget:

CheckWhat it says
Will not fitGerman is routinely a third longer than English. A label that wraps to more lines than its box holds, a caption wider than its button, a checkbox or dropdown text cut at its edge - each names the widget, the language, and what the text needs against what the widget has.
MissingHow many marked texts each language has no translation for. They will show in the base language.
Cannot be drawnEvery character the text's font does not have, by widget and language. On the device each one is an empty box.
Dropdown linesA translation with a different number of options from the base language.

Widths are measured the way the canvas measures them, which is within a few per cent of the panel rather than exact - a text reported as fitting by a pixel is worth a look in the language selector.

Accents and other alphabets

Accented text needs an imported font. LVGL's built-in Montserrat holds the English letters, digits, punctuation, the degree sign and a bullet - no "ä", "é", "ñ" or "ß", and no Greek, Cyrillic or Chinese. The fonts in the library are converted with their own limited sets. A character the font does not have draws as an empty box on the panel, with no error anywhere.

So that it cannot happen unnoticed:

The cure is a font that has the letters. Import one on the Fonts tab - Montserrat's own full file, Noto Sans, anything under a free licence - and set it on the text under Style settings > Text > Font. From there it is automatic: the characters your translations use are added to the font at export, in every size the design uses, with nothing to tick. Add a Polish translation and "ł" is in the next build.

If the file itself lacks a character - a Latin font asked for Chinese - the warning says the font file does not have it, which is a different problem with a different cure: a font for that script. Beyond the set chosen when it was imported, only the characters your texts use are converted, so a few dozen Chinese words cost a few dozen glyphs. The file you import can be 1.5 MB at most, though, and a complete Chinese, Japanese or Korean font is many times that: import a subset of one that holds the characters you need.

Right-to-left scripts are not supported yet. Arabic and Hebrew need LVGL's bidirectional text and Arabic shaping, which the export does not switch on, so they would draw left to right and unjoined.

Switching on the device

The action is Set language, in the Language group. It takes one of the design's languages, or the next one, which steps through them in the tab's order and goes from the last back to the first - a single button is then a language switch. It works from a widget's event and from an automation.

What a switch does on the panel:

In the Arduino export

A design with two or more languages gains ui_i18n.h and ui_i18n.c in the UI folder, on LVGL 9 and LVGL 8 alike. A design in one language gets neither, and its export is byte for byte what it was.

typedef enum {
    UI_LANG_EN = 0, // English
    UI_LANG_DE,     // German
    UI_LANG_COUNT
} ui_lang_t;

void         ui_set_language(ui_lang_t lang);
ui_lang_t    ui_get_language(void);
const char * ui_language_name(ui_lang_t lang);
const char * ui_text(ui_text_t id);

Each language is a table of strings in flash, and a text with no translation holds the base text there too - it was filled in when the file was written, so the device has no fallback to look up. From your own code:

ui_set_language(UI_LANG_DE);
lv_label_set_text(my_label, ui_language_name(ui_get_language()));

The choice is not kept across a reboot. To keep it, store ui_get_language() yourself - in Preferences, say - and call ui_set_language() with it before ui_init(), so the screens are built in that language rather than built and then changed. The export's README repeats this with your own languages' names.

If the export's Code prefix is not ui, the files and functions take it: hmi_i18n.h, hmi_set_language().

In the MicroPython export

The same thing as a module, ui_i18n.py: a tuple of strings for each language, set_language(), next_language(), get_language() and text(), and constants LANG_EN, LANG_DE. The firmware has Montserrat 12, 14 and 16 built in and nothing else, so a translated design with accents needs an imported font here too; it is written to /fonts with the characters your translations use, as on Arduino.

ESPHome

The ESPHome export writes the base language only. ESPHome's LVGL component has no table of texts to switch between, so a design with several languages exports as the one it was drawn in. The Languages tab and the Problems tab both say so, the other languages stay in the design, and Set language is greyed out with the reason beside it. Nothing is lost by keeping them: the same design exported for Arduino or MicroPython writes them all.

What it does not do