mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-21 14:32:44 +08:00
feat(translation): add translation support (#8255)
This commit is contained in:
committed by
GitHub
parent
cbe1f1eeca
commit
bca975cf22
@@ -21,6 +21,7 @@ extend-ignore-re = [
|
||||
"Rename lv_chart_clear_serie",
|
||||
"rename LV_ROLLER_MODE_INIFINITE",
|
||||
"CARD_INFO_SET\\(&img_multilang_avatar_.*\\)",
|
||||
"ist",
|
||||
"ACI\\)",
|
||||
]
|
||||
|
||||
|
||||
@@ -392,7 +392,7 @@ menu "LVGL configuration"
|
||||
bool "Enable VGLite asserts"
|
||||
depends on LV_USE_DRAW_VGLITE
|
||||
default n
|
||||
|
||||
|
||||
config LV_USE_VGLITE_CHECK_ERROR
|
||||
bool "Enable VGLite error checks"
|
||||
depends on LV_USE_DRAW_VGLITE
|
||||
@@ -1728,6 +1728,8 @@ menu "LVGL configuration"
|
||||
config LV_USE_TEST_SCREENSHOT_COMPARE
|
||||
bool "Enable `lv_test_screenshot_compare`. Requires libpng and a few MB of extra RAM."
|
||||
depends on LV_USE_TEST
|
||||
config LV_USE_TRANSLATION
|
||||
bool "Enable text translation support"
|
||||
config LV_USE_XML
|
||||
bool "Enable loading XML UIs runtime"
|
||||
config LV_USE_COLOR_FILTER
|
||||
|
||||
@@ -66,6 +66,7 @@ LV_USE_FILE_EXPLORER 1
|
||||
LV_USE_TEST 1
|
||||
LV_USE_TEST_SCREENSHOT_COMPARE 1
|
||||
LV_USE_XML 1
|
||||
LV_USE_TRANSLATION 1
|
||||
|
||||
LV_USE_SDL 1
|
||||
|
||||
|
||||
@@ -126,4 +126,5 @@ LV_USE_FONT_MANAGER 1
|
||||
LV_USE_TEST 1
|
||||
LV_USE_TEST_SCREENSHOT_COMPARE 1
|
||||
LV_USE_XML 1
|
||||
LV_USE_TRANSLATION 1
|
||||
LV_BUILD_EXAMPLES 1
|
||||
|
||||
@@ -19,4 +19,5 @@ Auxiliary Modules
|
||||
observer/index
|
||||
snapshot
|
||||
test
|
||||
translation
|
||||
xml/index
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
.. _translation:
|
||||
|
||||
===========
|
||||
Translation
|
||||
===========
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
LVGL supports two ways of handling translations:
|
||||
|
||||
- `lv_i18n <https://github.com/lvgl/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:
|
||||
|
||||
.. code-block:: c
|
||||
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:
|
||||
|
||||
:cpp:expr:`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:
|
||||
|
||||
- :cpp:expr:`lv_translation_get("tag")`
|
||||
- or the shorthand: :cpp:expr:`lv_tr("tag")`
|
||||
|
||||
These return a translated string which can be used with widgets:
|
||||
|
||||
.. code-block:: c
|
||||
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.
|
||||
|
||||
.. _lv_translation_example:
|
||||
|
||||
Example
|
||||
*******
|
||||
|
||||
.. include:: ../../examples/others/translation/index.rst
|
||||
|
||||
.. _lv_translation_api:
|
||||
|
||||
API
|
||||
***
|
||||
@@ -26,5 +26,7 @@ XML - Declarative UI
|
||||
events
|
||||
subjects
|
||||
animations
|
||||
translations
|
||||
translation
|
||||
license
|
||||
|
||||
license
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
.. _xml_translation:
|
||||
|
||||
============
|
||||
Translations
|
||||
============
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
The XML translation module allows defining and using translated strings directly within XML files.
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
Example XML translation definition:
|
||||
|
||||
.. code-block:: xml
|
||||
<translations languages="en de hu">
|
||||
<translation tag="dog" en="The dog" de="Der Hund" hu="A kutya"/>
|
||||
<translation tag="cat" en="The cat" de="Die Katze" hu="A cica"/>
|
||||
<translation tag="snake" en="A snake" de="Eine Schlange" hu="A kígyó"/>
|
||||
</translations>
|
||||
|
||||
In the root `<translations>` tag, the `languages` attribute defines the available languages,
|
||||
e.g., ``languages="en de hu"``. Language codes are free-form, but ISO-style codes are recommended.
|
||||
|
||||
Each `<translation>` defines a `tag`, which acts as the lookup key, and attributes for each language.
|
||||
|
||||
Translations may be omitted—:ref:`Fallbacks <xml_translations_fallback>` will be applied when needed.
|
||||
|
||||
To register XML translations:
|
||||
|
||||
- :cpp:expr:`lv_xml_translation_register_from_file("path/to/file.xml")`
|
||||
- :cpp:expr:`lv_xml_translation_register_from_data(xml_string)`
|
||||
|
||||
Multiple XML sources can be registered; they will be merged and searched collectively.
|
||||
|
||||
Usage in XML
|
||||
************
|
||||
|
||||
Some widget properties support a `*-translated` suffix to refer to translation tags. For example:
|
||||
|
||||
.. code-block:: xml
|
||||
<lv_label text-translated="dog"/>
|
||||
|
||||
This sets the label's text to the translated string for `"dog"`.
|
||||
|
||||
More Details
|
||||
************
|
||||
|
||||
For information on selecting the active language, retrieving translations, and fallback behavior,
|
||||
refer to the general :ref:`LVGL translation module <translation>`.
|
||||
@@ -1,7 +0,0 @@
|
||||
.. _xml_translations:
|
||||
|
||||
============
|
||||
Translations
|
||||
============
|
||||
|
||||
TODO
|
||||
@@ -24,6 +24,7 @@ extern "C" {
|
||||
#include "snapshot/lv_example_snapshot.h"
|
||||
#include "gestures/lv_example_gestures.h"
|
||||
#include "xml/lv_example_xml.h"
|
||||
#include "translation/lv_example_translation.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
#include "../../lv_examples.h"
|
||||
#if LV_USE_SNAPSHOT && LV_BUILD_EXAMPLES
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
Simple translation example
|
||||
---------------------------
|
||||
|
||||
.. lv_example:: others/translation/lv_example_translation_1
|
||||
:language: c
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @file lv_example_translation.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_EXAMPLE_TRANSLATION_H
|
||||
#define LV_EXAMPLE_TRANSLATION_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
void lv_example_translation_1(void);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_EXAMPLE_TRANSLATION_H*/
|
||||
@@ -0,0 +1,54 @@
|
||||
#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*/
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "../../lv_examples.h"
|
||||
#if LV_BUILD_EXAMPLES && LV_USE_XML
|
||||
#if LV_BUILD_EXAMPLES && LV_USE_XML && LV_USE_TRANSLATION
|
||||
|
||||
void lv_example_xml_2(void)
|
||||
{
|
||||
@@ -13,20 +13,27 @@ void lv_example_xml_2(void)
|
||||
}
|
||||
lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_card.xml");
|
||||
lv_xml_component_register_from_file("A:lvgl/examples/others/xml/my_button.xml");
|
||||
lv_xml_component_register_from_file("A:lvgl/examples/others/xml/view.xml");
|
||||
lv_xml_translation_register_from_file("A:lvgl/examples/others/xml/translations.xml");
|
||||
|
||||
lv_xml_register_font(NULL, "lv_montserrat_18", &lv_font_montserrat_18);
|
||||
|
||||
lv_subject_t s1;
|
||||
lv_subject_t s2;
|
||||
static char buf[200];
|
||||
lv_subject_init_string(&s1, buf, NULL, 200, "Waaaa");
|
||||
lv_subject_init_int(&s2, 25);
|
||||
lv_translation_set_language("de");
|
||||
|
||||
lv_xml_register_subject(NULL, "s1", &s2);
|
||||
lv_obj_t * obj = (lv_obj_t *) lv_xml_create(lv_screen_active(), "view", NULL);
|
||||
lv_obj_set_pos(obj, 10, 10);
|
||||
|
||||
lv_xml_test_register_from_file("A:lvgl/examples/others/xml/view.xml", "A:");
|
||||
lv_xml_component_unregister("my_button");
|
||||
|
||||
lv_xml_test_run_all(1);
|
||||
const char * slider_attrs[] = {
|
||||
"x", "200",
|
||||
"y", "-15",
|
||||
"align", "bottom_left",
|
||||
"value", "30",
|
||||
NULL, NULL,
|
||||
};
|
||||
|
||||
lv_obj_t * slider = (lv_obj_t *) lv_xml_create(lv_screen_active(), "lv_slider", slider_attrs);
|
||||
lv_obj_set_width(slider, 100);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -25,7 +25,10 @@
|
||||
</styles>
|
||||
|
||||
<view extends="lv_obj" style_radius="3" width="#size" height="content" styles="gray" style_bg_color="$bg_color" >
|
||||
<lv_label text="$title" align="left_mid"/>
|
||||
<my_button styles="$btn_rel_style $btn_pr_style:pressed" btn_text="$action" align="right_mid"/>
|
||||
<lv_label text-translated="$title" align="left_mid"/>
|
||||
<my_button btn_text="$action" align="right_mid">
|
||||
<style name="$btn_rel_style"/>
|
||||
<style name="$btn_pr_style" selector="pressed"/>
|
||||
</my_button>
|
||||
</view>
|
||||
</component>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<translations languages="en de hu">
|
||||
<translation tag="Card 1" en="Card 1" de="Karte 1" hu="Egy kártya"/>
|
||||
<translation tag="No title" en="No title" de="Kein Title"/>
|
||||
</translations>
|
||||
@@ -7,30 +7,16 @@
|
||||
<styles>
|
||||
<style name="btn_style" bg_color="#dark_blue" bg_opa="150"/>
|
||||
<style name="btn_pr_style" bg_opa="255"/>
|
||||
<style name="red_border" border_width="2" border_side="full" border_color="0xff0000"></style>
|
||||
</styles>
|
||||
|
||||
<subjects>
|
||||
<int name="subject1" value="10"/>
|
||||
</subjects>
|
||||
|
||||
|
||||
<view extends="lv_obj" flex_flow="column" name="main" width="280" height="content" style_bg_color="#light_blue">
|
||||
<lv_label bind_text="subject1" styles="btn_style:disabled red_border:checked">
|
||||
<bind_flag_if_gt subject="subject1" flag="hidden" ref_value="60"/>
|
||||
<bind_state_if_lt subject="subject1" state="checked" ref_value="40"/>
|
||||
<bind_state_if_lt subject="subject1" state="disabled" ref_value="20"/>
|
||||
<style name="btn_style" selector="disabled"/>
|
||||
<style name="red_border" selector="knob checked"/>
|
||||
</lv_label>
|
||||
<lv_slider bind_value="subject1" />
|
||||
<my_card title="Card 1" name="card1"
|
||||
y="100"
|
||||
styles="red_border"
|
||||
<view extends="lv_obj" name="main" width="280" height="content" style_bg_color="#light_blue">
|
||||
<lv_label text-translated="tiger"/>
|
||||
<my_card title="Card 1" name="card1"
|
||||
y="30"
|
||||
btn_rel_style="btn_style"
|
||||
btn_pr_style="btn_pr_style"/>
|
||||
|
||||
<my_card y="185"
|
||||
<my_card y="125"
|
||||
bg_color="0xffaaaa"
|
||||
action="Apply"
|
||||
btn_rel_style="btn_style"
|
||||
|
||||
+18
-14
@@ -1165,8 +1165,12 @@
|
||||
/** Enable loading XML UIs runtime */
|
||||
#define LV_USE_XML 0
|
||||
|
||||
/** 1: Enable text translation support */
|
||||
#define LV_USE_TRANSLATION 0
|
||||
|
||||
/*1: Enable color filter style*/
|
||||
#define LV_USE_COLOR_FILTER 0
|
||||
|
||||
/*==================
|
||||
* DEVICES
|
||||
*==================*/
|
||||
@@ -1340,10 +1344,10 @@
|
||||
#if LV_BUILD_DEMOS
|
||||
/** Show some widgets. This might be required to increase `LV_MEM_SIZE`. */
|
||||
#define LV_USE_DEMO_WIDGETS 0
|
||||
|
||||
|
||||
/** Demonstrate usage of encoder and keyboard. */
|
||||
#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0
|
||||
|
||||
|
||||
/** Benchmark your system */
|
||||
#define LV_USE_DEMO_BENCHMARK 0
|
||||
|
||||
@@ -1355,10 +1359,10 @@
|
||||
/** Render test for each primitive.
|
||||
* - Requires at least 480x272 display. */
|
||||
#define LV_USE_DEMO_RENDER 0
|
||||
|
||||
|
||||
/** Stress test for LVGL */
|
||||
#define LV_USE_DEMO_STRESS 0
|
||||
|
||||
|
||||
/** Music player demo */
|
||||
#define LV_USE_DEMO_MUSIC 0
|
||||
#if LV_USE_DEMO_MUSIC
|
||||
@@ -1368,38 +1372,38 @@
|
||||
#define LV_DEMO_MUSIC_LARGE 0
|
||||
#define LV_DEMO_MUSIC_AUTO_PLAY 0
|
||||
#endif
|
||||
|
||||
|
||||
/** Vector graphic demo */
|
||||
#define LV_USE_DEMO_VECTOR_GRAPHIC 0
|
||||
|
||||
|
||||
/*---------------------------
|
||||
* Demos from lvgl/lv_demos
|
||||
---------------------------*/
|
||||
|
||||
|
||||
/** Flex layout demo */
|
||||
#define LV_USE_DEMO_FLEX_LAYOUT 0
|
||||
|
||||
|
||||
/** Smart-phone like multi-language demo */
|
||||
#define LV_USE_DEMO_MULTILANG 0
|
||||
|
||||
|
||||
/** Widget transformation demo */
|
||||
#define LV_USE_DEMO_TRANSFORM 0
|
||||
|
||||
|
||||
/** Demonstrate scroll settings */
|
||||
#define LV_USE_DEMO_SCROLL 0
|
||||
|
||||
|
||||
/*E-bike demo with Lottie animations (if LV_USE_LOTTIE is enabled)*/
|
||||
#define LV_USE_DEMO_EBIKE 0
|
||||
#if LV_USE_DEMO_EBIKE
|
||||
#define LV_DEMO_EBIKE_PORTRAIT 0 /*0: for 480x270..480x320, 1: for 480x800..720x1280*/
|
||||
#endif
|
||||
|
||||
|
||||
/** High-resolution demo */
|
||||
#define LV_USE_DEMO_HIGH_RES 0
|
||||
|
||||
|
||||
/* Smart watch demo */
|
||||
#define LV_USE_DEMO_SMARTWATCH 0
|
||||
#endif /* LV_BUILD_DEMOS */
|
||||
#endif /* LV_BUILD_DEMOS */
|
||||
|
||||
/*--END OF LV_CONF_H--*/
|
||||
|
||||
|
||||
@@ -96,8 +96,8 @@ extern "C" {
|
||||
#include "src/others/ime/lv_ime_pinyin.h"
|
||||
#include "src/others/file_explorer/lv_file_explorer.h"
|
||||
#include "src/others/font_manager/lv_font_manager.h"
|
||||
#include "src/others/translation/lv_translation.h"
|
||||
#include "src/others/xml/lv_xml.h"
|
||||
#include "src/others/xml/lv_xml_component.h"
|
||||
#include "src/others/test/lv_test.h"
|
||||
|
||||
#include "src/libs/barcode/lv_barcode.h"
|
||||
|
||||
@@ -228,6 +228,11 @@ typedef struct _lv_global_t {
|
||||
lv_test_state_t test_state;
|
||||
#endif
|
||||
|
||||
#if LV_USE_TRANSLATION
|
||||
lv_ll_t translation_packs_ll;
|
||||
const char * translation_selected_lang;
|
||||
#endif
|
||||
|
||||
#if LV_USE_NUTTX
|
||||
struct _lv_nuttx_ctx_t * nuttx_ctx;
|
||||
#endif
|
||||
|
||||
+24
-14
@@ -3710,6 +3710,15 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/** 1: Enable text translation support */
|
||||
#ifndef LV_USE_TRANSLATION
|
||||
#ifdef CONFIG_LV_USE_TRANSLATION
|
||||
#define LV_USE_TRANSLATION CONFIG_LV_USE_TRANSLATION
|
||||
#else
|
||||
#define LV_USE_TRANSLATION 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*1: Enable color filter style*/
|
||||
#ifndef LV_USE_COLOR_FILTER
|
||||
#ifdef CONFIG_LV_USE_COLOR_FILTER
|
||||
@@ -3718,6 +3727,7 @@
|
||||
#define LV_USE_COLOR_FILTER 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*==================
|
||||
* DEVICES
|
||||
*==================*/
|
||||
@@ -4339,7 +4349,7 @@
|
||||
#define LV_USE_DEMO_WIDGETS 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/** Demonstrate usage of encoder and keyboard. */
|
||||
#ifndef LV_USE_DEMO_KEYPAD_AND_ENCODER
|
||||
#ifdef CONFIG_LV_USE_DEMO_KEYPAD_AND_ENCODER
|
||||
@@ -4348,7 +4358,7 @@
|
||||
#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/** Benchmark your system */
|
||||
#ifndef LV_USE_DEMO_BENCHMARK
|
||||
#ifdef CONFIG_LV_USE_DEMO_BENCHMARK
|
||||
@@ -4378,7 +4388,7 @@
|
||||
#define LV_USE_DEMO_RENDER 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/** Stress test for LVGL */
|
||||
#ifndef LV_USE_DEMO_STRESS
|
||||
#ifdef CONFIG_LV_USE_DEMO_STRESS
|
||||
@@ -4387,7 +4397,7 @@
|
||||
#define LV_USE_DEMO_STRESS 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/** Music player demo */
|
||||
#ifndef LV_USE_DEMO_MUSIC
|
||||
#ifdef CONFIG_LV_USE_DEMO_MUSIC
|
||||
@@ -4433,7 +4443,7 @@
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/** Vector graphic demo */
|
||||
#ifndef LV_USE_DEMO_VECTOR_GRAPHIC
|
||||
#ifdef CONFIG_LV_USE_DEMO_VECTOR_GRAPHIC
|
||||
@@ -4442,11 +4452,11 @@
|
||||
#define LV_USE_DEMO_VECTOR_GRAPHIC 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/*---------------------------
|
||||
* Demos from lvgl/lv_demos
|
||||
---------------------------*/
|
||||
|
||||
|
||||
/** Flex layout demo */
|
||||
#ifndef LV_USE_DEMO_FLEX_LAYOUT
|
||||
#ifdef CONFIG_LV_USE_DEMO_FLEX_LAYOUT
|
||||
@@ -4455,7 +4465,7 @@
|
||||
#define LV_USE_DEMO_FLEX_LAYOUT 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/** Smart-phone like multi-language demo */
|
||||
#ifndef LV_USE_DEMO_MULTILANG
|
||||
#ifdef CONFIG_LV_USE_DEMO_MULTILANG
|
||||
@@ -4464,7 +4474,7 @@
|
||||
#define LV_USE_DEMO_MULTILANG 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/** Widget transformation demo */
|
||||
#ifndef LV_USE_DEMO_TRANSFORM
|
||||
#ifdef CONFIG_LV_USE_DEMO_TRANSFORM
|
||||
@@ -4473,7 +4483,7 @@
|
||||
#define LV_USE_DEMO_TRANSFORM 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/** Demonstrate scroll settings */
|
||||
#ifndef LV_USE_DEMO_SCROLL
|
||||
#ifdef CONFIG_LV_USE_DEMO_SCROLL
|
||||
@@ -4482,7 +4492,7 @@
|
||||
#define LV_USE_DEMO_SCROLL 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/*E-bike demo with Lottie animations (if LV_USE_LOTTIE is enabled)*/
|
||||
#ifndef LV_USE_DEMO_EBIKE
|
||||
#ifdef CONFIG_LV_USE_DEMO_EBIKE
|
||||
@@ -4500,7 +4510,7 @@
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/** High-resolution demo */
|
||||
#ifndef LV_USE_DEMO_HIGH_RES
|
||||
#ifdef CONFIG_LV_USE_DEMO_HIGH_RES
|
||||
@@ -4509,7 +4519,7 @@
|
||||
#define LV_USE_DEMO_HIGH_RES 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* Smart watch demo */
|
||||
#ifndef LV_USE_DEMO_SMARTWATCH
|
||||
#ifdef CONFIG_LV_USE_DEMO_SMARTWATCH
|
||||
@@ -4518,7 +4528,7 @@
|
||||
#define LV_USE_DEMO_SMARTWATCH 0
|
||||
#endif
|
||||
#endif
|
||||
#endif /* LV_BUILD_DEMOS */
|
||||
#endif /* LV_BUILD_DEMOS */
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "misc/lv_fs.h"
|
||||
#include "osal/lv_os_private.h"
|
||||
#include "others/sysmon/lv_sysmon_private.h"
|
||||
#include "others/translation/lv_translation.h"
|
||||
#include "others/xml/lv_xml.h"
|
||||
|
||||
#if LV_USE_SVG
|
||||
@@ -393,6 +394,10 @@ void lv_init(void)
|
||||
lv_svg_decoder_init();
|
||||
#endif
|
||||
|
||||
#if LV_USE_TRANSLATION
|
||||
lv_translation_init();
|
||||
#endif
|
||||
|
||||
#if LV_USE_XML
|
||||
lv_xml_init();
|
||||
#endif
|
||||
@@ -510,6 +515,10 @@ void lv_deinit(void)
|
||||
lv_xml_test_unregister();
|
||||
#endif
|
||||
|
||||
#if LV_USE_TRANSLATION
|
||||
lv_translation_deinit();
|
||||
#endif
|
||||
|
||||
lv_mem_deinit();
|
||||
|
||||
lv_initialized = false;
|
||||
|
||||
@@ -383,6 +383,12 @@ typedef struct _lv_xml_parser_state_t lv_xml_parser_state_t;
|
||||
typedef struct _lv_evdev_discovery_t lv_evdev_discovery_t;
|
||||
#endif
|
||||
|
||||
#if LV_USE_TRANSLATION
|
||||
typedef struct _lv_translation_tag_dsc_t lv_translation_tag_dsc_t;
|
||||
|
||||
typedef struct _lv_translation_pack_t lv_translation_pack_t;
|
||||
#endif
|
||||
|
||||
#endif /*__ASSEMBLY__*/
|
||||
|
||||
/**********************
|
||||
|
||||
@@ -0,0 +1,283 @@
|
||||
/**
|
||||
* @file lv_translation.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_translation.h"
|
||||
#if LV_USE_TRANSLATION
|
||||
|
||||
#include "lv_translation_private.h"
|
||||
#include "../../misc/lv_ll.h"
|
||||
#include "../../stdlib/lv_mem.h"
|
||||
#include "../../stdlib/lv_string.h"
|
||||
#include "../../misc/lv_log.h"
|
||||
#include "../../misc/lv_assert.h"
|
||||
#include "../../core/lv_global.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#define packs_ll (LV_GLOBAL_DEFAULT()->translation_packs_ll)
|
||||
#define selected_lang (LV_GLOBAL_DEFAULT()->translation_selected_lang)
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void lv_translation_init(void)
|
||||
{
|
||||
lv_ll_init(&packs_ll, sizeof(lv_translation_pack_t));
|
||||
selected_lang = NULL;
|
||||
}
|
||||
|
||||
void lv_translation_deinit(void)
|
||||
{
|
||||
lv_translation_pack_t * pack;
|
||||
LV_LL_READ(&packs_ll, pack) {
|
||||
if(pack->is_static == false) {
|
||||
size_t i;
|
||||
size_t trans_cnt = lv_array_size(&pack->translation_array);
|
||||
for(i = 0; i < trans_cnt; i++) {
|
||||
lv_translation_tag_dsc_t * tag = lv_array_at(&pack->translation_array, i);
|
||||
lv_free((void *)tag->tag);
|
||||
|
||||
size_t j;
|
||||
for(j = 0; j < pack->language_cnt; j++) {
|
||||
lv_free((void *)tag->translations[j]); /*Free each translation of the tag*/
|
||||
}
|
||||
lv_free(tag->translations);
|
||||
}
|
||||
|
||||
lv_array_deinit(&pack->translation_array);
|
||||
|
||||
for(i = 0; i < pack->language_cnt; i++) {
|
||||
lv_free((void *)pack->languages[i]);
|
||||
}
|
||||
lv_free(pack->languages);
|
||||
}
|
||||
}
|
||||
|
||||
lv_ll_clear(&packs_ll);
|
||||
|
||||
lv_free((void *)selected_lang);
|
||||
}
|
||||
|
||||
lv_translation_pack_t * lv_translation_add_static(const char * languages[], const char * tags[],
|
||||
const char * translations[])
|
||||
{
|
||||
LV_ASSERT_NULL(languages);
|
||||
LV_ASSERT_NULL(tags);
|
||||
LV_ASSERT_NULL(translations);
|
||||
|
||||
lv_translation_pack_t * pack = lv_ll_ins_head(&packs_ll);
|
||||
LV_ASSERT_MALLOC(pack);
|
||||
if(pack == NULL) return NULL;
|
||||
lv_memzero(pack, sizeof(lv_translation_pack_t));
|
||||
pack->is_static = 1;
|
||||
|
||||
/*Count the languages*/
|
||||
while(languages[pack->language_cnt]) {
|
||||
pack->language_cnt++;
|
||||
}
|
||||
|
||||
pack->languages = languages;
|
||||
pack->tag_p = tags;
|
||||
pack->translation_p = translations;
|
||||
return pack;
|
||||
}
|
||||
|
||||
lv_translation_pack_t * lv_translation_add_dynamic(void)
|
||||
{
|
||||
lv_translation_pack_t * pack = lv_ll_ins_head(&packs_ll);
|
||||
LV_ASSERT_MALLOC(pack);
|
||||
if(pack == NULL) return NULL;
|
||||
|
||||
lv_memzero(pack, sizeof(lv_translation_pack_t));
|
||||
|
||||
pack->is_static = 0;
|
||||
lv_array_init(&pack->translation_array, 16, sizeof(lv_translation_tag_dsc_t));
|
||||
|
||||
return pack;
|
||||
}
|
||||
|
||||
void lv_translation_set_language(const char * lang)
|
||||
{
|
||||
if(selected_lang) lv_free((void *)selected_lang);
|
||||
selected_lang = lv_strdup(lang);
|
||||
}
|
||||
|
||||
const char * lv_translation_get(const char * tag)
|
||||
{
|
||||
if(selected_lang == NULL) {
|
||||
LV_LOG_WARN("No language is selected to get the translation of `%s`", tag);
|
||||
return tag;
|
||||
}
|
||||
|
||||
lv_translation_pack_t * pack;
|
||||
bool lang_found = false;
|
||||
LV_LL_READ(&packs_ll, pack) {
|
||||
uint32_t lang;
|
||||
for(lang = 0; lang < pack->language_cnt; lang++) {
|
||||
/*Does this pack contains the language?*/
|
||||
if(lv_streq(pack->languages[lang], selected_lang)) {
|
||||
lang_found = true;
|
||||
/*Find the tag*/
|
||||
if(pack->is_static) {
|
||||
uint32_t t;
|
||||
for(t = 0; pack->tag_p[t]; t++) {
|
||||
if(lv_streq(pack->tag_p[t], tag)) {
|
||||
/*Find the "row" of the tag */
|
||||
const char ** tr_row = pack->translation_p + pack->language_cnt * t;
|
||||
const char * tr = tr_row[lang];
|
||||
if(tr) return tr; /*Found directly*/
|
||||
|
||||
LV_LOG_WARN("`%s` tag is not found. Using the tag as translation.", tag);
|
||||
return tag; /*Return the tag as a fall back*/
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
size_t trans_cnt = lv_array_size(&pack->translation_array);
|
||||
size_t i;
|
||||
for(i = 0; i < trans_cnt; i++) {
|
||||
lv_translation_tag_dsc_t * tag_dsc = lv_array_at(&pack->translation_array, i);
|
||||
if(lv_streq(tag_dsc->tag, tag)) {
|
||||
const char * tr = tag_dsc->translations[lang];
|
||||
if(tr) return tr; /*Found directly*/
|
||||
|
||||
LV_LOG_WARN("`%s` tag is not found. Using the tag as translation.", tag);
|
||||
return tag; /*Return the tag as a worst case option*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(lang_found) {
|
||||
LV_LOG_WARN("`%s` tag is not found, using the tag as translation.", tag);
|
||||
}
|
||||
else {
|
||||
LV_LOG_WARN("`%s` language is not found, using the `%s` as translation.", selected_lang, tag);
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
lv_result_t lv_translation_add_language(lv_translation_pack_t * pack, const char * lang)
|
||||
{
|
||||
if(pack->is_static) {
|
||||
LV_LOG_WARN("Can't add language `%s` to static translation pack `%p`", lang, (void *)pack);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
pack->language_cnt++;
|
||||
pack->languages = lv_realloc(pack->languages, sizeof(const char *) * pack->language_cnt);
|
||||
LV_ASSERT_MALLOC(pack->languages);
|
||||
if(pack->languages == NULL) {
|
||||
LV_LOG_WARN("Couldn't allocate languages in `%p`", (void *)pack);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
pack->languages[pack->language_cnt - 1] = lv_strdup(lang);
|
||||
LV_ASSERT_MALLOC(pack->languages[pack->language_cnt - 1]);
|
||||
if(pack->languages[pack->language_cnt - 1] == NULL) {
|
||||
LV_LOG_WARN("Couldn't allocate the new language in `%p`", (void *)pack);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
int32_t lv_translation_get_language_index(lv_translation_pack_t * pack, const char * lang_name)
|
||||
{
|
||||
uint32_t i;
|
||||
for(i = 0; i < pack->language_cnt; i++) {
|
||||
if(lv_streq(pack->languages[i], lang_name)) return (int32_t)i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
lv_translation_tag_dsc_t * lv_translation_add_tag(lv_translation_pack_t * pack, const char * tag_name)
|
||||
{
|
||||
if(pack->is_static) {
|
||||
LV_LOG_WARN("Can't add tag `%s` to static translation pack `%p`", tag_name, (void *)pack);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
lv_translation_tag_dsc_t tag;
|
||||
tag.tag = lv_strdup(tag_name);
|
||||
LV_ASSERT_MALLOC(tag.tag);
|
||||
tag.translations = lv_zalloc(pack->language_cnt * sizeof(const char *));
|
||||
LV_ASSERT_MALLOC(tag.translations);
|
||||
|
||||
if(tag.tag == NULL || tag.translations == NULL) {
|
||||
LV_LOG_WARN("Couldn't allocate memory for the tag's data in `%p`", (void *)pack);
|
||||
lv_free((void *)tag.tag);
|
||||
lv_free((void *)tag.translations);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lv_result_t res = lv_array_push_back(&pack->translation_array, &tag);
|
||||
|
||||
if(res != LV_RESULT_OK) {
|
||||
LV_LOG_WARN("Couldn't add the tag in `%p`", (void *)pack);
|
||||
lv_free((void *)tag.tag);
|
||||
lv_free((void *)tag.translations);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return lv_array_back(&pack->translation_array);
|
||||
}
|
||||
|
||||
lv_result_t lv_translation_set_tag_translation(lv_translation_pack_t * pack, lv_translation_tag_dsc_t * tag,
|
||||
uint32_t lang_idx, const char * trans)
|
||||
{
|
||||
if(pack->is_static) {
|
||||
LV_LOG_WARN("Can't set tag translation`%s` in static translation pack `%p`", trans, (void *)pack);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
if(lang_idx >= pack->language_cnt) {
|
||||
|
||||
LV_LOG_WARN("Can't set the translation for language %" LV_PRIu32 " as there are only %" LV_PRIu32
|
||||
" languages defined in %p",
|
||||
lang_idx, pack->language_cnt, (void *)pack);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
lv_free((void *)tag->translations[lang_idx]); /*Free the earlier set language if any*/
|
||||
tag->translations[lang_idx] = lv_strdup(trans);
|
||||
if(tag->translations[lang_idx] == NULL) {
|
||||
LV_LOG_WARN("Couldn't allocate the new translation in tag `%p` in pack `%p`", (void *)tag, (void *) pack);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_TRANSLATION*/
|
||||
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* @file lv_translation.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_TRANSLATION_H
|
||||
#define LV_TRANSLATION_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../../lv_conf_internal.h"
|
||||
|
||||
#if LV_USE_TRANSLATION
|
||||
|
||||
#include LV_STDINT_INCLUDE
|
||||
#include "../../misc/lv_array.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Initialize the translation module
|
||||
*/
|
||||
void lv_translation_init(void);
|
||||
|
||||
/**
|
||||
* De-initialize the translation module and free all allocated translations
|
||||
*/
|
||||
void lv_translation_deinit(void);
|
||||
|
||||
/**
|
||||
* Register a translation pack from static arrays.
|
||||
* All the pointers need to be static, that is to live while they are used
|
||||
* @param languages List of languages. E.g. `{"en", "de", NULL}`
|
||||
* @param tags Tags that are using in the UI. E.g. `{"dog", "cat", NULL}`
|
||||
* @param translations List of translations. E.g. `{"Dog", "Cat", "Hund", "Katze"}`
|
||||
* @return The created pack
|
||||
*/
|
||||
lv_translation_pack_t * lv_translation_add_static(const char * languages[], const char * tags[],
|
||||
const char * translations[]);
|
||||
|
||||
/**
|
||||
* Add a pack to which translations can be added dynamically.
|
||||
* `pack->languages` needs to be a malloc-ed array where each language is also malloc-ed as an element.
|
||||
* `pack->translation_array` stores the translation having `lv_translation_tag_dsc_t` items
|
||||
* In each array element `tag` is a malloced string, `translations` is a malloc-ed array
|
||||
* with malloc-ed array for each element.
|
||||
* @return the created pack to which data can be added manually.
|
||||
*/
|
||||
lv_translation_pack_t * lv_translation_add_dynamic(void);
|
||||
|
||||
/**
|
||||
* Select the current language
|
||||
* @param lang a string from the defined languages. E.g. "en" or "de"
|
||||
*/
|
||||
void lv_translation_set_language(const char * lang);
|
||||
|
||||
/**
|
||||
* Get the translated version of a tag on the selected language
|
||||
* @param tag the tag to translate
|
||||
* @return the translation
|
||||
* @note fallback rules:
|
||||
* - if the tag is found on the selected language return it
|
||||
* - if the tag is not found on the selected language, use the fist language
|
||||
* - if the tag is not found on the first language, return the tag
|
||||
*/
|
||||
const char * lv_translation_get(const char * tag);
|
||||
|
||||
/**
|
||||
* Shorthand of lv_translation_set_language
|
||||
* @param tag the tag to translate
|
||||
* @return the translation
|
||||
*/
|
||||
static inline const char * lv_tr(const char * tag)
|
||||
{
|
||||
return lv_translation_get(tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new language to a dynamic language pack.
|
||||
* All languages should be added before adding tags
|
||||
* @param pack pointer to a dynamic translation pack
|
||||
* @param lang language to add, e.g. "en", or "de"
|
||||
* @return LV_RESULT_OK: success, LV_RESULT_INVALID: failed
|
||||
*/
|
||||
lv_result_t lv_translation_add_language(lv_translation_pack_t * pack, const char * lang);
|
||||
|
||||
/**
|
||||
* Get the index of a language in a pack.
|
||||
* @param pack pointer to a static or dynamic language pack
|
||||
* @param lang_name name of the language to find
|
||||
* @return index of the language or -1 if not found.
|
||||
*/
|
||||
int32_t lv_translation_get_language_index(lv_translation_pack_t * pack, const char * lang_name);
|
||||
|
||||
/**
|
||||
* Add a new tag to a dynamic language pack.
|
||||
* Once the tag is added the translations for each language can be added too by using
|
||||
* `lv_translation_set_tag_translation`
|
||||
* @param pack pointer to a dynamic translation pack
|
||||
* @param tag_name name of the tag, e.g. "dog", or "house"
|
||||
* @return pointer to the allocated tag descriptor
|
||||
*/
|
||||
lv_translation_tag_dsc_t * lv_translation_add_tag(lv_translation_pack_t * pack, const char * tag_name);
|
||||
|
||||
/**
|
||||
* Add a translation to a tag in a dynamic translation pack
|
||||
* @param pack pointer to a dynamic translation pack
|
||||
* @param tag return value of `lv_translation_add_tag`
|
||||
* @param lang_idx index of the language for which translation should be set
|
||||
* @param trans the translation on the given language
|
||||
* @return LV_RESULT_OK: success, LV_RESULT_INVALID: failed
|
||||
*/
|
||||
lv_result_t lv_translation_set_tag_translation(lv_translation_pack_t * pack, lv_translation_tag_dsc_t * tag,
|
||||
uint32_t lang_idx, const char * trans);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
#endif /*LV_USE_TRANSLATION*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /* LV_TRANSLATION_H */
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* @file lv_translation_private.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_TRANSLATION_PRIVATE_H
|
||||
#define LV_TRANSLATION_PRIVATE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../../lv_conf_internal.h"
|
||||
|
||||
#if LV_USE_TRANSLATION
|
||||
|
||||
#include LV_STDINT_INCLUDE
|
||||
#include "../../misc/lv_array.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
struct _lv_translation_tag_dsc_t {
|
||||
const char * tag;
|
||||
const char ** translations; /**< Translations for each language*/
|
||||
};
|
||||
|
||||
struct _lv_translation_pack_t {
|
||||
const char ** languages;
|
||||
uint32_t language_cnt;
|
||||
uint32_t is_static; /*In the union translations_p is used*/
|
||||
const char ** tag_p;
|
||||
const char ** translation_p; /*E.g. {{"a", "b"}, {"c", "d"}}*/
|
||||
lv_array_t translation_array;
|
||||
};
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_USE_TRANSLATION*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /* LV_TRANSLATION_PRIVATE_H */
|
||||
@@ -10,13 +10,14 @@
|
||||
#include "lv_xml.h"
|
||||
#if LV_USE_XML
|
||||
|
||||
#include "lv_xml.h"
|
||||
#include "lv_xml_base_types.h"
|
||||
#include "lv_xml_parser.h"
|
||||
#include "lv_xml_component.h"
|
||||
#include "lv_xml_component_private.h"
|
||||
#include "lv_xml_widget.h"
|
||||
#include "lv_xml_style.h"
|
||||
#include "lv_xml.h"
|
||||
#include "lv_xml_translation.h"
|
||||
#include "lv_xml_utils.h"
|
||||
#include "lv_xml_private.h"
|
||||
#include "parsers/lv_xml_obj_parser.h"
|
||||
|
||||
@@ -14,11 +14,13 @@ extern "C" {
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../../misc/lv_types.h"
|
||||
#include "../../misc/lv_event.h"
|
||||
#include "../../others/observer/lv_observer.h"
|
||||
|
||||
#if LV_USE_XML
|
||||
#include "../../misc/lv_event.h"
|
||||
#include "../../others/observer/lv_observer.h"
|
||||
#include "lv_xml_test.h"
|
||||
#include "lv_xml_translation.h"
|
||||
#include "lv_xml_component.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/**
|
||||
* @file lv_xml_translation.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../../lvgl.h"
|
||||
#if LV_USE_XML && LV_USE_TRANSLATION
|
||||
|
||||
#include "../translation/lv_translation_private.h"
|
||||
#include "lv_xml_widget.h"
|
||||
#include "lv_xml_parser.h"
|
||||
#include "../../others/translation/lv_translation.h"
|
||||
#include "../../libs/expat/expat.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
static void start_handler(void * user_data, const char * name, const char ** attrs);
|
||||
static void end_handler(void * user_data, const char * name);
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
lv_result_t lv_xml_translation_register_from_file(const char * path)
|
||||
{
|
||||
lv_fs_res_t fs_res;
|
||||
lv_fs_file_t f;
|
||||
fs_res = lv_fs_open(&f, path, LV_FS_MODE_RD);
|
||||
if(fs_res != LV_FS_RES_OK) {
|
||||
LV_LOG_WARN("Couldn't open %s", path);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
/* Determine file size */
|
||||
lv_fs_seek(&f, 0, LV_FS_SEEK_END);
|
||||
uint32_t file_size = 0;
|
||||
lv_fs_tell(&f, &file_size);
|
||||
lv_fs_seek(&f, 0, LV_FS_SEEK_SET);
|
||||
|
||||
/* Create the buffer */
|
||||
char * xml_buf = lv_malloc(file_size + 1);
|
||||
LV_ASSERT_MALLOC(xml_buf);
|
||||
if(xml_buf == NULL) {
|
||||
LV_LOG_WARN("Memory allocation failed for file %s (%d bytes)", path, file_size + 1);
|
||||
lv_fs_close(&f);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
/* Read the file content */
|
||||
uint32_t rn;
|
||||
lv_fs_read(&f, xml_buf, file_size, &rn);
|
||||
if(rn != file_size) {
|
||||
LV_LOG_WARN("Couldn't read %s fully", path);
|
||||
lv_free(xml_buf);
|
||||
lv_fs_close(&f);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
/* Null-terminate the buffer */
|
||||
xml_buf[rn] = '\0';
|
||||
|
||||
/* Register the component */
|
||||
lv_result_t res = lv_xml_translation_register_from_data(xml_buf);
|
||||
|
||||
/* Housekeeping */
|
||||
lv_free(xml_buf);
|
||||
lv_fs_close(&f);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
lv_result_t lv_xml_translation_register_from_data(const char * xml_def)
|
||||
{
|
||||
lv_translation_pack_t * pack = lv_translation_add_dynamic();
|
||||
|
||||
/* Parse the XML to extract metadata */
|
||||
XML_Parser parser = XML_ParserCreate(NULL);
|
||||
XML_SetUserData(parser, pack);
|
||||
XML_SetElementHandler(parser, start_handler, end_handler);
|
||||
|
||||
if(XML_Parse(parser, xml_def, lv_strlen(xml_def), XML_TRUE) == XML_STATUS_ERROR) {
|
||||
LV_LOG_ERROR("XML parsing error: %s on line %lu",
|
||||
XML_ErrorString(XML_GetErrorCode(parser)),
|
||||
(unsigned long)XML_GetCurrentLineNumber(parser));
|
||||
XML_ParserFree(parser);
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
XML_ParserFree(parser);
|
||||
return LV_RESULT_OK;
|
||||
}
|
||||
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void start_handler(void * user_data, const char * name, const char ** attrs)
|
||||
{
|
||||
lv_translation_pack_t * pack = user_data;
|
||||
|
||||
if(lv_streq(name, "translations")) {
|
||||
const char * languages = lv_xml_get_value_of(attrs, "languages");
|
||||
if(languages == NULL) {
|
||||
LV_LOG_WARN("`languages` are not set in `translations`");
|
||||
return;
|
||||
}
|
||||
char buf[512];
|
||||
char * bufp = buf;
|
||||
lv_strlcpy(buf, languages, sizeof(buf));
|
||||
bufp = buf;
|
||||
lv_result_t res = LV_RESULT_OK;
|
||||
while(bufp[0]) {
|
||||
const char * lang = lv_xml_split_str(&bufp, ' ');
|
||||
res = lv_translation_add_language(pack, lang);
|
||||
if(res != LV_RESULT_OK) {
|
||||
LV_LOG_WARN("Couldn't add language `%s`", lang);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(lv_streq(name, "translation")) {
|
||||
if(pack->language_cnt == 0 || pack->languages == NULL) {
|
||||
LV_LOG_WARN("`No languages were found, <translations languages=\"...\" was not set>`");
|
||||
return;
|
||||
}
|
||||
const char * tag_name = lv_xml_get_value_of(attrs, "tag");
|
||||
if(tag_name == NULL) {
|
||||
LV_LOG_WARN("`tag` is missing from the translation");
|
||||
return;
|
||||
}
|
||||
|
||||
lv_translation_tag_dsc_t * tag = lv_translation_add_tag(pack, tag_name);
|
||||
LV_ASSERT_NULL(tag);
|
||||
if(tag == NULL) {
|
||||
LV_LOG_WARN("Couldn't add tag `%s`", tag_name);
|
||||
return;
|
||||
}
|
||||
uint32_t i;
|
||||
for(i = 0; i < pack->language_cnt; i++) {
|
||||
const char * trans = lv_xml_get_value_of(attrs, pack->languages[i]);
|
||||
if(trans == NULL) {
|
||||
LV_LOG_WARN("`%s` language is missing from tag `%s`", pack->languages[i], tag_name);
|
||||
continue;
|
||||
}
|
||||
lv_result_t res = lv_translation_set_tag_translation(pack, tag, i, trans);
|
||||
if(res != LV_RESULT_OK) {
|
||||
LV_LOG_WARN("Couldn't set translation `%s` in tag `%s`", trans, tag_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void end_handler(void * user_data, const char * name)
|
||||
{
|
||||
LV_UNUSED(user_data);
|
||||
LV_UNUSED(name);
|
||||
}
|
||||
|
||||
#endif /* LV_USE_XML */
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @file lv_xml_translation.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_XML_TRANSLATION_H
|
||||
#define LV_XML_TRANSLATION_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "../../misc/lv_types.h"
|
||||
#if LV_USE_XML && LV_USE_TRANSLATION
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* Register translations from an XML file
|
||||
* @param path path to an XML file (staring with a driver letter)
|
||||
* @return LV_RES_OK: no error
|
||||
*/
|
||||
lv_result_t lv_xml_translation_register_from_file(const char * path);
|
||||
|
||||
/**
|
||||
* Register translations from an XML string
|
||||
* @param xml_def the XML definition as a string
|
||||
* @return LV_RES_OK: no error
|
||||
*/
|
||||
lv_result_t lv_xml_translation_register_from_data(const char * xml_def);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#endif /* LV_USE_XML */
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_XML_TRANSLATION_H*/
|
||||
|
||||
|
||||
@@ -58,6 +58,10 @@ void lv_xml_label_apply(lv_xml_parser_state_t * state, const char ** attrs)
|
||||
|
||||
if(lv_streq("text", name)) lv_label_set_text(item, value);
|
||||
else if(lv_streq("long_mode", name)) lv_label_set_long_mode(item, long_mode_text_to_enum_value(value));
|
||||
#if LV_USE_TRANSLATION
|
||||
if(lv_streq("text-translated", name)) lv_label_set_text(item, lv_tr(value));
|
||||
#endif
|
||||
if(lv_streq("long_mode", name)) lv_label_set_long_mode(item, long_mode_text_to_enum_value(value));
|
||||
else if(lv_streq("bind_text", name)) {
|
||||
lv_subject_t * subject = lv_xml_get_subject(&state->scope, value);
|
||||
if(subject == NULL) {
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
#define LV_PROFILER_INCLUDE "lv_profiler_builtin.h"
|
||||
#define LV_USE_GRIDNAV 1
|
||||
#define LV_USE_XML 1
|
||||
#define LV_USE_TRANSLATION 1
|
||||
#define LV_USE_TEST 1
|
||||
#define LV_USE_TEST_SCREENSHOT_COMPARE 1
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#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());
|
||||
}
|
||||
|
||||
void test_xml_translation(void)
|
||||
{
|
||||
const char * translations_xml = {
|
||||
"<translations languages=\"en de hu\">"
|
||||
" <translation tag=\"dog\" en=\"The dog\" de=\"Der Hund\" hu=\"A kutya\"/>"
|
||||
" <translation tag=\"cat\" en=\"The cat\" hu=\"A cica\"/>"
|
||||
" <translation tag=\"snake\" en=\"A snake\" de=\"Eine Schlange\" hu=\"A kígyó\"/>"
|
||||
"</translations>"
|
||||
};
|
||||
|
||||
lv_xml_translation_register_from_data(translations_xml);
|
||||
|
||||
|
||||
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_pack_t * pack = lv_translation_add_static(languages, tags, translations);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, lv_translation_get_language_index(pack, "de"));
|
||||
TEST_ASSERT_EQUAL_INT(-1, lv_translation_get_language_index(pack, "none"));
|
||||
|
||||
lv_translation_set_language("de");
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING("Der Hund", lv_translation_get("dog"));
|
||||
TEST_ASSERT_EQUAL_STRING("Der Löwe", lv_translation_get("lion"));
|
||||
|
||||
/*The tag the fallback if not defined for the selected language*/
|
||||
TEST_ASSERT_EQUAL_STRING("cat", lv_translation_get("cat"));
|
||||
|
||||
/*Use the tag if the tag is not found*/
|
||||
TEST_ASSERT_EQUAL_STRING("foo", lv_tr("foo"));
|
||||
|
||||
lv_translation_set_language("es");
|
||||
|
||||
TEST_ASSERT_EQUAL_STRING("El Conejo", lv_tr("rabbit"));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -5,7 +5,7 @@ Example
|
||||
|
||||
<widget>
|
||||
<api>
|
||||
<enumdef name="lv_buttonamtrix_ctrl" multi="true" help="control flags for the buttons">
|
||||
<enumdef name="lv_buttonmatrix_ctrl" multi="true" help="control flags for the buttons">
|
||||
<enum name="none" help="No special control"/>
|
||||
<enum name="width_1" help="Relative width is 1 in its row"/>
|
||||
<enum name="width_2" help="Relative width is 2 in its row"/>
|
||||
|
||||
+2
-1
@@ -13,7 +13,8 @@ Example
|
||||
<enum name="dots" help=""/>
|
||||
</enumdef>
|
||||
|
||||
<prop name="text" type="string" />
|
||||
<prop name="text" type="string" />
|
||||
<prop name="text-translated" type="string" />
|
||||
<prop name="long_mode" type="enum:lv_label_long_mode" />
|
||||
<prop name="bind_text">
|
||||
<param name="bind_text" type="subject" />
|
||||
|
||||
+3
-2
@@ -26,7 +26,7 @@ Example
|
||||
<enum name="adv_hittest" help="Allow performing more accurate hit (click) test. E.g. consider rounded corners."/>
|
||||
<enum name="ignore_layout" help="Make the object not positioned by the layouts"/>
|
||||
<enum name="floating" help="Do not scroll the object when the parent scrolls and ignore layout"/>
|
||||
<enum name="send_draw_task_evenTS" help="Send `LV_EVENT_DRAW_TASK_ADDED` events"/>
|
||||
<enum name="send_draw_task_events" help="Send `LV_EVENT_DRAW_TASK_ADDED` events"/>
|
||||
<enum name="overflow_visible" help="Do not clip the children to the parent's ext draw size"/>
|
||||
<enum name="flex_in_new_track" help="Start a new flex track on this item"/>
|
||||
<enum name="layout_1" help="Custom flag, free to use by layouts"/>
|
||||
@@ -61,7 +61,8 @@ Example
|
||||
<element name="event_cb" access="add">
|
||||
<arg name="name" type="string"/>
|
||||
<arg name="trigger" type="lv_event" default="clicked"/>
|
||||
<arg name="cb" type="event_cb"/>
|
||||
<arg name="callback" type="event_cb"/>
|
||||
<arg name="user_data" type="string" default="NULL"/>
|
||||
</element>
|
||||
|
||||
<element name="screen_load_event" access="add">
|
||||
|
||||
Reference in New Issue
Block a user