diff --git a/docs/src/details/auxiliary-modules/translation.rst b/docs/src/details/auxiliary-modules/translation.rst index 490c74ebb5..2503226d4e 100644 --- a/docs/src/details/auxiliary-modules/translation.rst +++ b/docs/src/details/auxiliary-modules/translation.rst @@ -84,6 +84,28 @@ 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 :cpp:expr:`lv_translation_set_language("language")` is called, LVGL sends ``LV_EVENT_TRANSLATION_LANGUAGE_CHANGED`` to every widget, allowing you to update text automatically. + +Basic Example +------------- + +.. code-block:: c + + 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)); + } + + 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. + .. _lv_translation_example: diff --git a/examples/others/translation/index.rst b/examples/others/translation/index.rst index 9e76596435..cfb58a42ba 100644 --- a/examples/others/translation/index.rst +++ b/examples/others/translation/index.rst @@ -4,4 +4,10 @@ Simple translation example .. lv_example:: others/translation/lv_example_translation_1 :language: c +Dynamic language selection +-------------------------- + +.. lv_example:: others/translation/lv_example_translation_2 + :language: c + diff --git a/examples/others/translation/lv_example_translation.h b/examples/others/translation/lv_example_translation.h index 10041737e6..8660787394 100644 --- a/examples/others/translation/lv_example_translation.h +++ b/examples/others/translation/lv_example_translation.h @@ -26,6 +26,7 @@ extern "C" { * GLOBAL PROTOTYPES **********************/ void lv_example_translation_1(void); +void lv_example_translation_2(void); /********************** * MACROS diff --git a/examples/others/translation/lv_example_translation_2.c b/examples/others/translation/lv_example_translation_2.c new file mode 100644 index 0000000000..eda5700a2e --- /dev/null +++ b/examples/others/translation/lv_example_translation_2.c @@ -0,0 +1,73 @@ +#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*/ diff --git a/src/misc/lv_event.c b/src/misc/lv_event.c index 696dfcb1ac..70e092ab7a 100644 --- a/src/misc/lv_event.c +++ b/src/misc/lv_event.c @@ -371,6 +371,10 @@ const char * lv_event_code_get_name(lv_event_code_t code) ENUM_CASE(EVENT_VSYNC); ENUM_CASE(EVENT_VSYNC_REQUEST); +#if LV_USE_TRANSLATION + ENUM_CASE(EVENT_TRANSLATION_LANGUAGE_CHANGED); +#endif /*LV_USE_TRANSLATION*/ + /* Special event flags */ case LV_EVENT_LAST: case LV_EVENT_PREPROCESS: diff --git a/src/misc/lv_event.h b/src/misc/lv_event.h index c8a66f9c9e..c2e86c1744 100644 --- a/src/misc/lv_event.h +++ b/src/misc/lv_event.h @@ -114,6 +114,9 @@ typedef enum { LV_EVENT_VSYNC, LV_EVENT_VSYNC_REQUEST, +#if LV_USE_TRANSLATION + LV_EVENT_TRANSLATION_LANGUAGE_CHANGED, /**< Sent when the translation language changed. */ +#endif /*LV_USE_TRANSLATION*/ LV_EVENT_LAST, /** Number of default events */ diff --git a/src/others/translation/lv_translation.c b/src/others/translation/lv_translation.c index b41711f1d0..54ed553dd8 100644 --- a/src/others/translation/lv_translation.c +++ b/src/others/translation/lv_translation.c @@ -16,6 +16,7 @@ #include "../../misc/lv_log.h" #include "../../misc/lv_assert.h" #include "../../core/lv_global.h" +#include "../../lvgl_private.h" /********************* * DEFINES @@ -31,6 +32,8 @@ * STATIC PROTOTYPES **********************/ +static lv_obj_tree_walk_res_t send_language_change_event(lv_obj_t * obj, void * lang); + /********************** * STATIC VARIABLES **********************/ @@ -119,10 +122,16 @@ lv_translation_pack_t * lv_translation_add_dynamic(void) return pack; } +const char * lv_translation_get_language(void) +{ + return selected_lang; +} + void lv_translation_set_language(const char * lang) { if(selected_lang) lv_free((void *)selected_lang); selected_lang = lv_strdup(lang); + lv_obj_tree_walk(NULL, send_language_change_event, (void *)lang); } const char * lv_translation_get(const char * tag) @@ -280,4 +289,10 @@ lv_result_t lv_translation_set_tag_translation(lv_translation_pack_t * pack, lv_ * STATIC FUNCTIONS **********************/ +static lv_obj_tree_walk_res_t send_language_change_event(lv_obj_t * obj, void * lang) +{ + lv_obj_send_event(obj, LV_EVENT_TRANSLATION_LANGUAGE_CHANGED, lang); + return LV_OBJ_TREE_WALK_NEXT; +} + #endif /*LV_USE_TRANSLATION*/ diff --git a/src/others/translation/lv_translation.h b/src/others/translation/lv_translation.h index 759d35cda3..84e8db9183 100644 --- a/src/others/translation/lv_translation.h +++ b/src/others/translation/lv_translation.h @@ -65,10 +65,17 @@ lv_translation_pack_t * lv_translation_add_dynamic(void); /** * Select the current language + * The `LV_EVENT_TRANSLATION_LANGUAGE_CHANGED` event will be sent to every widget * @param lang a string from the defined languages. E.g. "en" or "de" */ void lv_translation_set_language(const char * lang); +/** + * Get the current selected language + * @return the current selected language + */ +const char * lv_translation_get_language(void); + /** * Get the translated version of a tag on the selected language * @param tag the tag to translate diff --git a/tests/src/test_cases/test_translation.c b/tests/src/test_cases/test_translation.c new file mode 100644 index 0000000000..eaa9666032 --- /dev/null +++ b/tests/src/test_cases/test_translation.c @@ -0,0 +1,52 @@ +#if LV_BUILD_TEST +#include "../lvgl.h" + +#include "unity/unity.h" + +void setUp(void) +{ + /* Function run before every test */ +} + +void tearDown(void) +{ + /* Function run after every test */ + lv_obj_clean(lv_screen_active()); +} + +static void on_language_change(lv_event_t * e) +{ + lv_obj_t * label = lv_event_get_target_obj(e); + const char * tag = lv_event_get_user_data(e); + const char * language = lv_event_get_param(e); + + lv_label_set_text(label, lv_tr(tag)); + TEST_ASSERT_EQUAL_STRING(language, lv_translation_get_language()); +} + +void test_set_language_sends_language_changed_event(void) +{ + + static const char * tags[] = {"tiger", NULL}; + static const char * languages[] = {"en", "de", "es", NULL}; + static const char * translations[] = { "The Tiger", "Der Tiger", "El Tigre" }; + lv_translation_add_static(languages, tags, translations); + + lv_obj_t * label = lv_label_create(NULL); + lv_obj_add_event_cb(label, on_language_change, LV_EVENT_TRANSLATION_LANGUAGE_CHANGED, "tiger"); + + lv_translation_set_language("en"); + TEST_ASSERT_EQUAL_STRING(lv_label_get_text(label), "The Tiger"); + + lv_translation_set_language("de"); + TEST_ASSERT_EQUAL_STRING(lv_label_get_text(label), "Der Tiger"); + + lv_translation_set_language("es"); + TEST_ASSERT_EQUAL_STRING(lv_label_get_text(label), "El Tigre"); + + /* Unknown language translates to the tag */ + lv_translation_set_language("fr"); + TEST_ASSERT_EQUAL_STRING(lv_label_get_text(label), "tiger"); +} + +#endif