add lv_i18n support
This commit is contained in:
5
Makefile
Normal file
5
Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
extract:
|
||||
lv_i18n extract -s 'src/**/*.+(c|cpp|h|hpp)' -t 'translations/*.yml'
|
||||
|
||||
compile:
|
||||
lv_i18n compile -t 'translations/*.yml' -o 'src/lv_i18n'
|
221
src/lv_i18n/lv_i18n.c
Normal file
221
src/lv_i18n/lv_i18n.c
Normal file
@@ -0,0 +1,221 @@
|
||||
#include "./lv_i18n.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Define plural operands
|
||||
// http://unicode.org/reports/tr35/tr35-numbers.html#Operands
|
||||
|
||||
// Integer version, simplified
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
static inline uint32_t op_n(int32_t val) { return (uint32_t)(val < 0 ? -val : val); }
|
||||
static inline uint32_t op_i(uint32_t val) { return val; }
|
||||
// always zero, when decimal part not exists.
|
||||
static inline uint32_t op_v(uint32_t val) { UNUSED(val); return 0;}
|
||||
static inline uint32_t op_w(uint32_t val) { UNUSED(val); return 0; }
|
||||
static inline uint32_t op_f(uint32_t val) { UNUSED(val); return 0; }
|
||||
static inline uint32_t op_t(uint32_t val) { UNUSED(val); return 0; }
|
||||
|
||||
static lv_i18n_phrase_t en_gb_singulars[] = {
|
||||
{"appName", "OS-Railway EN"},
|
||||
{NULL, NULL} // End mark
|
||||
};
|
||||
|
||||
|
||||
|
||||
static uint8_t en_gb_plural_fn(int32_t num)
|
||||
{
|
||||
uint32_t n = op_n(num); UNUSED(n);
|
||||
uint32_t i = op_i(n); UNUSED(i);
|
||||
uint32_t v = op_v(n); UNUSED(v);
|
||||
|
||||
if ((i == 1 && v == 0)) return LV_I18N_PLURAL_TYPE_ONE;
|
||||
return LV_I18N_PLURAL_TYPE_OTHER;
|
||||
}
|
||||
|
||||
static const lv_i18n_lang_t en_gb_lang = {
|
||||
.locale_name = "en-GB",
|
||||
.singulars = en_gb_singulars,
|
||||
|
||||
.locale_plural_fn = en_gb_plural_fn
|
||||
};
|
||||
|
||||
static lv_i18n_phrase_t de_de_singulars[] = {
|
||||
{"appName", "OS-Railway DE"},
|
||||
{NULL, NULL} // End mark
|
||||
};
|
||||
|
||||
|
||||
|
||||
static uint8_t de_de_plural_fn(int32_t num)
|
||||
{
|
||||
uint32_t n = op_n(num); UNUSED(n);
|
||||
uint32_t i = op_i(n); UNUSED(i);
|
||||
uint32_t v = op_v(n); UNUSED(v);
|
||||
|
||||
if ((i == 1 && v == 0)) return LV_I18N_PLURAL_TYPE_ONE;
|
||||
return LV_I18N_PLURAL_TYPE_OTHER;
|
||||
}
|
||||
|
||||
static const lv_i18n_lang_t de_de_lang = {
|
||||
.locale_name = "de-DE",
|
||||
.singulars = de_de_singulars,
|
||||
|
||||
.locale_plural_fn = de_de_plural_fn
|
||||
};
|
||||
|
||||
const lv_i18n_language_pack_t lv_i18n_language_pack[] = {
|
||||
&en_gb_lang,
|
||||
&de_de_lang,
|
||||
NULL // End mark
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
// Internal state
|
||||
static const lv_i18n_language_pack_t * current_lang_pack;
|
||||
static const lv_i18n_lang_t * current_lang;
|
||||
|
||||
|
||||
/**
|
||||
* Reset internal state. For testing.
|
||||
*/
|
||||
void __lv_i18n_reset(void)
|
||||
{
|
||||
current_lang_pack = NULL;
|
||||
current_lang = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the languages for internationalization
|
||||
* @param langs pointer to the array of languages. (Last element has to be `NULL`)
|
||||
*/
|
||||
int lv_i18n_init(const lv_i18n_language_pack_t * langs)
|
||||
{
|
||||
if(langs == NULL) return -1;
|
||||
if(langs[0] == NULL) return -1;
|
||||
|
||||
current_lang_pack = langs;
|
||||
current_lang = langs[0]; /*Automatically select the first language*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the localization (language)
|
||||
* @param l_name name of the translation locale to use. E.g. "en-GB"
|
||||
*/
|
||||
int lv_i18n_set_locale(const char * l_name)
|
||||
{
|
||||
if(current_lang_pack == NULL) return -1;
|
||||
|
||||
uint16_t i;
|
||||
|
||||
for(i = 0; current_lang_pack[i] != NULL; i++) {
|
||||
// Found -> finish
|
||||
if(strcmp(current_lang_pack[i]->locale_name, l_name) == 0) {
|
||||
current_lang = current_lang_pack[i];
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static const char * __lv_i18n_get_text_core(lv_i18n_phrase_t * trans, const char * msg_id)
|
||||
{
|
||||
uint16_t i;
|
||||
for(i = 0; trans[i].msg_id != NULL; i++) {
|
||||
if(strcmp(trans[i].msg_id, msg_id) == 0) {
|
||||
/*The msg_id has found. Check the translation*/
|
||||
if(trans[i].translation) return trans[i].translation;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the translation from a message ID
|
||||
* @param msg_id message ID
|
||||
* @return the translation of `msg_id` on the set local
|
||||
*/
|
||||
const char * lv_i18n_get_text(const char * msg_id)
|
||||
{
|
||||
if(current_lang == NULL) return msg_id;
|
||||
|
||||
const lv_i18n_lang_t * lang = current_lang;
|
||||
const void * txt;
|
||||
|
||||
// Search in current locale
|
||||
if(lang->singulars != NULL) {
|
||||
txt = __lv_i18n_get_text_core(lang->singulars, msg_id);
|
||||
if (txt != NULL) return txt;
|
||||
}
|
||||
|
||||
// Try to fallback
|
||||
if(lang == current_lang_pack[0]) return msg_id;
|
||||
lang = current_lang_pack[0];
|
||||
|
||||
// Repeat search for default locale
|
||||
if(lang->singulars != NULL) {
|
||||
txt = __lv_i18n_get_text_core(lang->singulars, msg_id);
|
||||
if (txt != NULL) return txt;
|
||||
}
|
||||
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the translation from a message ID and apply the language's plural rule to get correct form
|
||||
* @param msg_id message ID
|
||||
* @param num an integer to select the correct plural form
|
||||
* @return the translation of `msg_id` on the set local
|
||||
*/
|
||||
const char * lv_i18n_get_text_plural(const char * msg_id, int32_t num)
|
||||
{
|
||||
if(current_lang == NULL) return msg_id;
|
||||
|
||||
const lv_i18n_lang_t * lang = current_lang;
|
||||
const void * txt;
|
||||
lv_i18n_plural_type_t ptype;
|
||||
|
||||
// Search in current locale
|
||||
if(lang->locale_plural_fn != NULL) {
|
||||
ptype = lang->locale_plural_fn(num);
|
||||
|
||||
if(lang->plurals[ptype] != NULL) {
|
||||
txt = __lv_i18n_get_text_core(lang->plurals[ptype], msg_id);
|
||||
if (txt != NULL) return txt;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to fallback
|
||||
if(lang == current_lang_pack[0]) return msg_id;
|
||||
lang = current_lang_pack[0];
|
||||
|
||||
// Repeat search for default locale
|
||||
if(lang->locale_plural_fn != NULL) {
|
||||
ptype = lang->locale_plural_fn(num);
|
||||
|
||||
if(lang->plurals[ptype] != NULL) {
|
||||
txt = __lv_i18n_get_text_core(lang->plurals[ptype], msg_id);
|
||||
if (txt != NULL) return txt;
|
||||
}
|
||||
}
|
||||
|
||||
return msg_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the currently used locale.
|
||||
* @return name of the currently used locale. E.g. "en-GB"
|
||||
*/
|
||||
const char * lv_i18n_get_current_locale(void)
|
||||
{
|
||||
if(!current_lang) return NULL;
|
||||
return current_lang->locale_name;
|
||||
}
|
85
src/lv_i18n/lv_i18n.h
Normal file
85
src/lv_i18n/lv_i18n.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#ifndef LV_I18N_H
|
||||
#define LV_I18N_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef enum {
|
||||
LV_I18N_PLURAL_TYPE_ZERO,
|
||||
LV_I18N_PLURAL_TYPE_ONE,
|
||||
LV_I18N_PLURAL_TYPE_TWO,
|
||||
LV_I18N_PLURAL_TYPE_FEW,
|
||||
LV_I18N_PLURAL_TYPE_MANY,
|
||||
LV_I18N_PLURAL_TYPE_OTHER,
|
||||
_LV_I18N_PLURAL_TYPE_NUM,
|
||||
} lv_i18n_plural_type_t;
|
||||
|
||||
typedef struct {
|
||||
const char * msg_id;
|
||||
const char * translation;
|
||||
} lv_i18n_phrase_t;
|
||||
|
||||
typedef struct {
|
||||
const char * locale_name;
|
||||
lv_i18n_phrase_t * singulars;
|
||||
lv_i18n_phrase_t * plurals[_LV_I18N_PLURAL_TYPE_NUM];
|
||||
uint8_t (*locale_plural_fn)(int32_t num);
|
||||
} lv_i18n_lang_t;
|
||||
|
||||
// Null-terminated list of languages. First one used as default.
|
||||
typedef const lv_i18n_lang_t * lv_i18n_language_pack_t;
|
||||
|
||||
|
||||
extern const lv_i18n_language_pack_t lv_i18n_language_pack[];
|
||||
|
||||
|
||||
/**
|
||||
* Set the languages for internationalization
|
||||
* @param langs pointer to the array of languages. (Last element has to be `NULL`)
|
||||
*/
|
||||
int lv_i18n_init(const lv_i18n_language_pack_t * langs);
|
||||
|
||||
/**
|
||||
* Change the localization (language)
|
||||
* @param l_name name of the translation locale to use. E.g. "en_GB"
|
||||
*/
|
||||
int lv_i18n_set_locale(const char * l_name);
|
||||
|
||||
/**
|
||||
* Get the translation from a message ID
|
||||
* @param msg_id message ID
|
||||
* @return the translation of `msg_id` on the set local
|
||||
*/
|
||||
const char * lv_i18n_get_text(const char * msg_id);
|
||||
|
||||
/**
|
||||
* Get the translation from a message ID and apply the language's plural rule to get correct form
|
||||
* @param msg_id message ID
|
||||
* @param num an integer to select the correct plural form
|
||||
* @return the translation of `msg_id` on the set local
|
||||
*/
|
||||
const char * lv_i18n_get_text_plural(const char * msg_id, int32_t num);
|
||||
|
||||
/**
|
||||
* Get the name of the currently used localization.
|
||||
* @return name of the currently used localization. E.g. "en_GB"
|
||||
*/
|
||||
const char * lv_i18n_get_current_locale(void);
|
||||
|
||||
|
||||
void __lv_i18n_reset(void);
|
||||
|
||||
|
||||
#define _(text) lv_i18n_get_text(text)
|
||||
#define _p(text, num) lv_i18n_get_text_plural(text, num)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /*LV_LANG_H*/
|
@@ -19,10 +19,14 @@
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "gfx/lv_setup.h"
|
||||
#include "lv_i18n/lv_i18n.h"
|
||||
#include "ui/launch_screen.h"
|
||||
|
||||
void setup()
|
||||
{
|
||||
lv_i18n_init(lv_i18n_language_pack);
|
||||
lv_i18n_set_locale("de-DE");
|
||||
|
||||
lv_begin();
|
||||
ui_LaunchScreen_open();
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#include "ui/launch_screen.h"
|
||||
|
||||
#include <lvgl.h>
|
||||
#include "lv_i18n/lv_i18n.h"
|
||||
|
||||
void ui_LaunchScreen_open()
|
||||
{
|
||||
@@ -14,7 +15,7 @@ void ui_LaunchScreen_open()
|
||||
lv_obj_align(img, LV_ALIGN_CENTER, 0, 0);
|
||||
|
||||
auto *label = lv_label_create(ui_SplashScreen);
|
||||
lv_label_set_text(label, "OS-Railway");
|
||||
lv_label_set_text(label, _("appName"));
|
||||
lv_obj_align(label, LV_ALIGN_BOTTOM_MID, 0, -25);
|
||||
|
||||
lv_disp_load_scr(ui_SplashScreen);
|
||||
|
2
translations/de-DE.yml
Normal file
2
translations/de-DE.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
de-DE:
|
||||
appName: OS-Railway DE
|
2
translations/en-GB.yml
Normal file
2
translations/en-GB.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
en-GB:
|
||||
appName: OS-Railway EN
|
Reference in New Issue
Block a user