mirror of
https://github.com/lvgl/lvgl.git
synced 2026-06-02 01:18:04 +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_chart_clear_serie",
|
||||||
"rename LV_ROLLER_MODE_INIFINITE",
|
"rename LV_ROLLER_MODE_INIFINITE",
|
||||||
"CARD_INFO_SET\\(&img_multilang_avatar_.*\\)",
|
"CARD_INFO_SET\\(&img_multilang_avatar_.*\\)",
|
||||||
|
"ist",
|
||||||
"ACI\\)",
|
"ACI\\)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1728,6 +1728,8 @@ menu "LVGL configuration"
|
|||||||
config LV_USE_TEST_SCREENSHOT_COMPARE
|
config LV_USE_TEST_SCREENSHOT_COMPARE
|
||||||
bool "Enable `lv_test_screenshot_compare`. Requires libpng and a few MB of extra RAM."
|
bool "Enable `lv_test_screenshot_compare`. Requires libpng and a few MB of extra RAM."
|
||||||
depends on LV_USE_TEST
|
depends on LV_USE_TEST
|
||||||
|
config LV_USE_TRANSLATION
|
||||||
|
bool "Enable text translation support"
|
||||||
config LV_USE_XML
|
config LV_USE_XML
|
||||||
bool "Enable loading XML UIs runtime"
|
bool "Enable loading XML UIs runtime"
|
||||||
config LV_USE_COLOR_FILTER
|
config LV_USE_COLOR_FILTER
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ LV_USE_FILE_EXPLORER 1
|
|||||||
LV_USE_TEST 1
|
LV_USE_TEST 1
|
||||||
LV_USE_TEST_SCREENSHOT_COMPARE 1
|
LV_USE_TEST_SCREENSHOT_COMPARE 1
|
||||||
LV_USE_XML 1
|
LV_USE_XML 1
|
||||||
|
LV_USE_TRANSLATION 1
|
||||||
|
|
||||||
LV_USE_SDL 1
|
LV_USE_SDL 1
|
||||||
|
|
||||||
|
|||||||
@@ -126,4 +126,5 @@ LV_USE_FONT_MANAGER 1
|
|||||||
LV_USE_TEST 1
|
LV_USE_TEST 1
|
||||||
LV_USE_TEST_SCREENSHOT_COMPARE 1
|
LV_USE_TEST_SCREENSHOT_COMPARE 1
|
||||||
LV_USE_XML 1
|
LV_USE_XML 1
|
||||||
|
LV_USE_TRANSLATION 1
|
||||||
LV_BUILD_EXAMPLES 1
|
LV_BUILD_EXAMPLES 1
|
||||||
|
|||||||
@@ -19,4 +19,5 @@ Auxiliary Modules
|
|||||||
observer/index
|
observer/index
|
||||||
snapshot
|
snapshot
|
||||||
test
|
test
|
||||||
|
translation
|
||||||
xml/index
|
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
|
events
|
||||||
subjects
|
subjects
|
||||||
animations
|
animations
|
||||||
translations
|
translation
|
||||||
|
license
|
||||||
|
|
||||||
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 "snapshot/lv_example_snapshot.h"
|
||||||
#include "gestures/lv_example_gestures.h"
|
#include "gestures/lv_example_gestures.h"
|
||||||
#include "xml/lv_example_xml.h"
|
#include "xml/lv_example_xml.h"
|
||||||
|
#include "translation/lv_example_translation.h"
|
||||||
|
|
||||||
/*********************
|
/*********************
|
||||||
* DEFINES
|
* DEFINES
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
#include "../../lv_examples.h"
|
#include "../../lv_examples.h"
|
||||||
#if LV_USE_SNAPSHOT && LV_BUILD_EXAMPLES
|
#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"
|
#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)
|
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_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/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_xml_register_font(NULL, "lv_montserrat_18", &lv_font_montserrat_18);
|
||||||
|
|
||||||
lv_subject_t s1;
|
lv_translation_set_language("de");
|
||||||
lv_subject_t s2;
|
|
||||||
static char buf[200];
|
|
||||||
lv_subject_init_string(&s1, buf, NULL, 200, "Waaaa");
|
|
||||||
lv_subject_init_int(&s2, 25);
|
|
||||||
|
|
||||||
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
|
#endif
|
||||||
|
|||||||
@@ -25,7 +25,10 @@
|
|||||||
</styles>
|
</styles>
|
||||||
|
|
||||||
<view extends="lv_obj" style_radius="3" width="#size" height="content" styles="gray" style_bg_color="$bg_color" >
|
<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"/>
|
<lv_label text-translated="$title" align="left_mid"/>
|
||||||
<my_button styles="$btn_rel_style $btn_pr_style:pressed" btn_text="$action" align="right_mid"/>
|
<my_button btn_text="$action" align="right_mid">
|
||||||
|
<style name="$btn_rel_style"/>
|
||||||
|
<style name="$btn_pr_style" selector="pressed"/>
|
||||||
|
</my_button>
|
||||||
</view>
|
</view>
|
||||||
</component>
|
</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>
|
<styles>
|
||||||
<style name="btn_style" bg_color="#dark_blue" bg_opa="150"/>
|
<style name="btn_style" bg_color="#dark_blue" bg_opa="150"/>
|
||||||
<style name="btn_pr_style" bg_opa="255"/>
|
<style name="btn_pr_style" bg_opa="255"/>
|
||||||
<style name="red_border" border_width="2" border_side="full" border_color="0xff0000"></style>
|
|
||||||
</styles>
|
</styles>
|
||||||
|
|
||||||
<subjects>
|
<view extends="lv_obj" name="main" width="280" height="content" style_bg_color="#light_blue">
|
||||||
<int name="subject1" value="10"/>
|
<lv_label text-translated="tiger"/>
|
||||||
</subjects>
|
<my_card title="Card 1" name="card1"
|
||||||
|
y="30"
|
||||||
|
|
||||||
<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"
|
|
||||||
btn_rel_style="btn_style"
|
btn_rel_style="btn_style"
|
||||||
btn_pr_style="btn_pr_style"/>
|
btn_pr_style="btn_pr_style"/>
|
||||||
|
|
||||||
<my_card y="185"
|
<my_card y="125"
|
||||||
bg_color="0xffaaaa"
|
bg_color="0xffaaaa"
|
||||||
action="Apply"
|
action="Apply"
|
||||||
btn_rel_style="btn_style"
|
btn_rel_style="btn_style"
|
||||||
|
|||||||
@@ -1165,8 +1165,12 @@
|
|||||||
/** Enable loading XML UIs runtime */
|
/** Enable loading XML UIs runtime */
|
||||||
#define LV_USE_XML 0
|
#define LV_USE_XML 0
|
||||||
|
|
||||||
|
/** 1: Enable text translation support */
|
||||||
|
#define LV_USE_TRANSLATION 0
|
||||||
|
|
||||||
/*1: Enable color filter style*/
|
/*1: Enable color filter style*/
|
||||||
#define LV_USE_COLOR_FILTER 0
|
#define LV_USE_COLOR_FILTER 0
|
||||||
|
|
||||||
/*==================
|
/*==================
|
||||||
* DEVICES
|
* DEVICES
|
||||||
*==================*/
|
*==================*/
|
||||||
|
|||||||
@@ -96,8 +96,8 @@ extern "C" {
|
|||||||
#include "src/others/ime/lv_ime_pinyin.h"
|
#include "src/others/ime/lv_ime_pinyin.h"
|
||||||
#include "src/others/file_explorer/lv_file_explorer.h"
|
#include "src/others/file_explorer/lv_file_explorer.h"
|
||||||
#include "src/others/font_manager/lv_font_manager.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.h"
|
||||||
#include "src/others/xml/lv_xml_component.h"
|
|
||||||
#include "src/others/test/lv_test.h"
|
#include "src/others/test/lv_test.h"
|
||||||
|
|
||||||
#include "src/libs/barcode/lv_barcode.h"
|
#include "src/libs/barcode/lv_barcode.h"
|
||||||
|
|||||||
@@ -228,6 +228,11 @@ typedef struct _lv_global_t {
|
|||||||
lv_test_state_t test_state;
|
lv_test_state_t test_state;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if LV_USE_TRANSLATION
|
||||||
|
lv_ll_t translation_packs_ll;
|
||||||
|
const char * translation_selected_lang;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if LV_USE_NUTTX
|
#if LV_USE_NUTTX
|
||||||
struct _lv_nuttx_ctx_t * nuttx_ctx;
|
struct _lv_nuttx_ctx_t * nuttx_ctx;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -3710,6 +3710,15 @@
|
|||||||
#endif
|
#endif
|
||||||
#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*/
|
/*1: Enable color filter style*/
|
||||||
#ifndef LV_USE_COLOR_FILTER
|
#ifndef LV_USE_COLOR_FILTER
|
||||||
#ifdef CONFIG_LV_USE_COLOR_FILTER
|
#ifdef CONFIG_LV_USE_COLOR_FILTER
|
||||||
@@ -3718,6 +3727,7 @@
|
|||||||
#define LV_USE_COLOR_FILTER 0
|
#define LV_USE_COLOR_FILTER 0
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*==================
|
/*==================
|
||||||
* DEVICES
|
* DEVICES
|
||||||
*==================*/
|
*==================*/
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
#include "misc/lv_fs.h"
|
#include "misc/lv_fs.h"
|
||||||
#include "osal/lv_os_private.h"
|
#include "osal/lv_os_private.h"
|
||||||
#include "others/sysmon/lv_sysmon_private.h"
|
#include "others/sysmon/lv_sysmon_private.h"
|
||||||
|
#include "others/translation/lv_translation.h"
|
||||||
#include "others/xml/lv_xml.h"
|
#include "others/xml/lv_xml.h"
|
||||||
|
|
||||||
#if LV_USE_SVG
|
#if LV_USE_SVG
|
||||||
@@ -393,6 +394,10 @@ void lv_init(void)
|
|||||||
lv_svg_decoder_init();
|
lv_svg_decoder_init();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if LV_USE_TRANSLATION
|
||||||
|
lv_translation_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
#if LV_USE_XML
|
#if LV_USE_XML
|
||||||
lv_xml_init();
|
lv_xml_init();
|
||||||
#endif
|
#endif
|
||||||
@@ -510,6 +515,10 @@ void lv_deinit(void)
|
|||||||
lv_xml_test_unregister();
|
lv_xml_test_unregister();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if LV_USE_TRANSLATION
|
||||||
|
lv_translation_deinit();
|
||||||
|
#endif
|
||||||
|
|
||||||
lv_mem_deinit();
|
lv_mem_deinit();
|
||||||
|
|
||||||
lv_initialized = false;
|
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;
|
typedef struct _lv_evdev_discovery_t lv_evdev_discovery_t;
|
||||||
#endif
|
#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__*/
|
#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"
|
#include "lv_xml.h"
|
||||||
#if LV_USE_XML
|
#if LV_USE_XML
|
||||||
|
|
||||||
|
#include "lv_xml.h"
|
||||||
#include "lv_xml_base_types.h"
|
#include "lv_xml_base_types.h"
|
||||||
#include "lv_xml_parser.h"
|
#include "lv_xml_parser.h"
|
||||||
#include "lv_xml_component.h"
|
#include "lv_xml_component.h"
|
||||||
#include "lv_xml_component_private.h"
|
#include "lv_xml_component_private.h"
|
||||||
#include "lv_xml_widget.h"
|
#include "lv_xml_widget.h"
|
||||||
#include "lv_xml_style.h"
|
#include "lv_xml_style.h"
|
||||||
#include "lv_xml.h"
|
#include "lv_xml_translation.h"
|
||||||
#include "lv_xml_utils.h"
|
#include "lv_xml_utils.h"
|
||||||
#include "lv_xml_private.h"
|
#include "lv_xml_private.h"
|
||||||
#include "parsers/lv_xml_obj_parser.h"
|
#include "parsers/lv_xml_obj_parser.h"
|
||||||
|
|||||||
@@ -14,11 +14,13 @@ extern "C" {
|
|||||||
* INCLUDES
|
* INCLUDES
|
||||||
*********************/
|
*********************/
|
||||||
#include "../../misc/lv_types.h"
|
#include "../../misc/lv_types.h"
|
||||||
#include "../../misc/lv_event.h"
|
|
||||||
#include "../../others/observer/lv_observer.h"
|
|
||||||
|
|
||||||
#if LV_USE_XML
|
#if LV_USE_XML
|
||||||
|
#include "../../misc/lv_event.h"
|
||||||
|
#include "../../others/observer/lv_observer.h"
|
||||||
#include "lv_xml_test.h"
|
#include "lv_xml_test.h"
|
||||||
|
#include "lv_xml_translation.h"
|
||||||
|
#include "lv_xml_component.h"
|
||||||
|
|
||||||
/*********************
|
/*********************
|
||||||
* DEFINES
|
* 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);
|
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));
|
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)) {
|
else if(lv_streq("bind_text", name)) {
|
||||||
lv_subject_t * subject = lv_xml_get_subject(&state->scope, value);
|
lv_subject_t * subject = lv_xml_get_subject(&state->scope, value);
|
||||||
if(subject == NULL) {
|
if(subject == NULL) {
|
||||||
|
|||||||
@@ -110,6 +110,7 @@
|
|||||||
#define LV_PROFILER_INCLUDE "lv_profiler_builtin.h"
|
#define LV_PROFILER_INCLUDE "lv_profiler_builtin.h"
|
||||||
#define LV_USE_GRIDNAV 1
|
#define LV_USE_GRIDNAV 1
|
||||||
#define LV_USE_XML 1
|
#define LV_USE_XML 1
|
||||||
|
#define LV_USE_TRANSLATION 1
|
||||||
#define LV_USE_TEST 1
|
#define LV_USE_TEST 1
|
||||||
#define LV_USE_TEST_SCREENSHOT_COMPARE 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>
|
<widget>
|
||||||
<api>
|
<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="none" help="No special control"/>
|
||||||
<enum name="width_1" help="Relative width is 1 in its row"/>
|
<enum name="width_1" help="Relative width is 1 in its row"/>
|
||||||
<enum name="width_2" help="Relative width is 2 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=""/>
|
<enum name="dots" help=""/>
|
||||||
</enumdef>
|
</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="long_mode" type="enum:lv_label_long_mode" />
|
||||||
<prop name="bind_text">
|
<prop name="bind_text">
|
||||||
<param name="bind_text" type="subject" />
|
<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="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="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="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="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="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"/>
|
<enum name="layout_1" help="Custom flag, free to use by layouts"/>
|
||||||
@@ -61,7 +61,8 @@ Example
|
|||||||
<element name="event_cb" access="add">
|
<element name="event_cb" access="add">
|
||||||
<arg name="name" type="string"/>
|
<arg name="name" type="string"/>
|
||||||
<arg name="trigger" type="lv_event" default="clicked"/>
|
<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>
|
||||||
|
|
||||||
<element name="screen_load_event" access="add">
|
<element name="screen_load_event" access="add">
|
||||||
|
|||||||
Reference in New Issue
Block a user