Translation

Overview

LVGL supports two ways of handling translations:

  • lv_i18n: A comprehensive tool that extracts translatable strings from C files into YAML files, and generates C translation files from them. It also supports plural forms. See its README for details.

  • lv_translation: A simpler yet more flexible solution that allows adding translations statically or dynamically. This is the method documented here.

Add Translations

Static Translations

If most translations are known at compile time, they can be defined using string arrays:

static const char * languages[] = {"en", "de", "es", NULL};
static const char * tags[] = {"tiger", "lion", "rabbit", "elephant", NULL};
static const char * translations[] = {
    "The Tiger", "Der Tiger", "El Tigre",
    "The Lion", "Der Löwe", "El León",
    "The Rabbit", "Das Kaninchen", "El Conejo",
    "The Elephant", "Der Elefant", "El Elefante",
};

lv_translation_add_static(languages, tags, translations);

This method uses only a little extra RAM, as only the pointers to the strings are stored.

Dynamic Translations

If translations are only available at runtime (e.g., from files, serial ports, or online sources), they can be added dynamically.

This approach involves memory allocation. See the example at the bottom of this page for reference.

Select a Language

Once translations are registered, use:

lv_translation_set_language("language")

to set the current language. The parameter must match one of the language names provided during registration.

Translate Strings

To retrieve a translation for a given tag, use:

These return a translated string which can be used with widgets:

lv_label_set_text(label, lv_tr("settings"));
lv_dropdown_set_options(dd, lv_tr("color_list"));

Fallbacks

If a tag exists but the translation for the selected language is missing the tag itself will be returned.

If the tag is not found at all, the tag itself will be used as a fallback as well.

Dynamically Updating UI Text

When lv_translation_set_language("language") is called, LVGL sends LV_EVENT_TRANSLATION_LANGUAGE_CHANGED to every widget, allowing you to update text automatically.

The new language can be retrieved by either calling lv_translation_get_language() or by getting the event parameter in the event callback with lv_event_get_param(e)

Basic Example

static void on_language_change(lv_event_t * e)
{
    lv_obj_t * label = lv_event_get_target_obj(e);
    const char * tag = (const char *) lv_event_get_user_data(e);
    lv_label_set_text(label, lv_tr(tag));

    /* You can get the new language with `lv_event_get_param`*/
    /* const char * language = (const char *) lv_event_get_param(e); */
    /* or with `lv_translation_get_language` */
    /* const char * language = lv_translation_get_language(); */
}

lv_obj_t * label = lv_label_create(lv_screen_active());
lv_obj_add_event_cb(label, on_language_change, LV_EVENT_TRANSLATION_LANGUAGE_CHANGED, "tag1");

See the the bottom of this page for a complete example.

Example

Simple translation example

#include "../../lv_examples.h"
#if LV_USE_TRANSLATION && LV_BUILD_EXAMPLES

static void add_static(void)
{
    static const char * languages[] = {"en", "de", "es", NULL};
    static const char * tags[] = {"tiger", "lion", "rabbit", "elephant", NULL};
    static const char * translations[] = {
        "The Tiger", "Der Tiger", "El Tigre",
        "The Lion", "Der Löwe", "El León",
        "The Rabbit", "Das Kaninchen", "El Conejo",
        "The Elephant", "Der Elefant", "El Elefante",
    };

    lv_translation_add_static(languages, tags, translations);
}

static void add_dynamic(void)
{
    lv_translation_pack_t * pack = lv_translation_add_dynamic();
    lv_translation_add_language(pack, "en");
    lv_translation_add_language(pack, "de");

    lv_translation_tag_dsc_t * tag;
    tag = lv_translation_add_tag(pack, "table");
    lv_translation_set_tag_translation(pack, tag, 0, "It's a table");
    lv_translation_set_tag_translation(pack, tag, 1, "Das is ein Tish");

    tag = lv_translation_add_tag(pack, "chair");
    lv_translation_set_tag_translation(pack, tag, 0, "It's a chair");
    lv_translation_set_tag_translation(pack, tag, 1, "Das ist ein Stuhl");
}

/**
 * Create and use translations
 */
void lv_example_translation_1(void)
{
    add_static();
    add_dynamic();

    lv_translation_set_language("de");

    lv_obj_t * label;

    label = lv_label_create(lv_screen_active());
    lv_label_set_text(label, lv_tr("tiger"));

    label = lv_label_create(lv_screen_active());
    lv_label_set_text(label, lv_tr("chair"));
    lv_obj_set_y(label, 50);
}

#endif /*LV_USE_TRANSLATION && LV_BUILD_EXAMPLES*/

Dynamic language selection

#include "../../lv_examples.h"

#if LV_USE_TRANSLATION && LV_USE_DROPDOWN && LV_USE_LABEL && LV_BUILD_EXAMPLES

static const char * tags[] = {"tiger", "lion", "rabbit", "elephant", NULL};
static const char * languages[] = {"English", "Deutsch", "Español", NULL};

static void add_static_translations(void)
{
    static const char * translations[] = {
        "The Tiger",    "Der Tiger",     "El Tigre",
        "The Lion",     "Der Löwe",      "El León",
        "The Rabbit",   "Das Kaninchen", "El Conejo",
        "The Elephant", "Der Elefant",   "El Elefante",
    };

    lv_translation_add_static(languages, tags, translations);
}

static void on_language_change(lv_event_t * e)
{
    lv_obj_t * label      = lv_event_get_target_obj(e);
    const char * tag      = (const char *) lv_event_get_user_data(e);
    /* You can get the new language with `lv_event_get_param`*/
    const char * language = (const char *) lv_event_get_param(e);
    LV_UNUSED(language);

    lv_label_set_text(label, lv_tr(tag));
}

static void language_change_cb(lv_event_t * e)
{
    static char selected_lang[20];

    lv_obj_t * dropdown = lv_event_get_target_obj(e);
    lv_dropdown_get_selected_str(dropdown, selected_lang, sizeof(selected_lang));
    lv_translation_set_language(selected_lang);
}

/**
 * Change label text when the translation language changes
 */
void lv_example_translation_2(void)
{
    lv_obj_set_flex_flow(lv_screen_active(), LV_FLEX_FLOW_COLUMN);
    lv_obj_set_flex_align(lv_screen_active(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);

    add_static_translations();
    const size_t tag_count = sizeof(tags) / sizeof(tags[0]) - 1;
    const size_t lang_count = sizeof(languages) / sizeof(languages[0]) - 1;

    /* Create a dropdown to be able to select the language */
    lv_obj_t * language_dropdown = lv_dropdown_create(lv_screen_active());
    lv_dropdown_clear_options(language_dropdown);

    for(size_t i = 0; i < lang_count; ++i) {
        lv_dropdown_add_option(language_dropdown, languages[i], i);
    }

    lv_obj_add_event_cb(language_dropdown, language_change_cb, LV_EVENT_VALUE_CHANGED, NULL);

    /* Create a label for each tag */
    for(size_t i = 0; i < tag_count; ++i) {
        lv_obj_t * label = lv_label_create(lv_screen_active());

        /* Bind to the language change event so that we can change the label when the language changes */
        lv_obj_add_event_cb(label, on_language_change, LV_EVENT_TRANSLATION_LANGUAGE_CHANGED, (void *)tags[i]);
    }

    lv_translation_set_language("English");
}

#endif /*LV_USE_TRANSLATION && LV_USE_DROPDOWN && LV_USE_LABEL && LV_BUILD_EXAMPLES*/

API

lv_translation.h

lv_translation_private.h