From 1c96444084e5e3ef268928758bca519296e337f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=8D=E5=90=83=E6=B2=B9=E7=82=B8=E9=B8=A1?= <1425962791@qq.com> Date: Wed, 28 Jan 2026 15:48:59 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E5=9B=BD=E9=99=85=E5=8C=96?= =?UTF-8?q?=EF=BC=88=E7=BF=BB=E8=AF=91=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Power_Pico/User/GUI/common/lv_i18n.c | 229 +++++++++++++++++++++++++++ Power_Pico/User/GUI/common/lv_i18n.h | 85 ++++++++++ Power_Pico/User/GUI/ui.c | 4 + Power_Pico/User/GUI/ui.h | 1 + 4 files changed, 319 insertions(+) create mode 100644 Power_Pico/User/GUI/common/lv_i18n.c create mode 100644 Power_Pico/User/GUI/common/lv_i18n.h diff --git a/Power_Pico/User/GUI/common/lv_i18n.c b/Power_Pico/User/GUI/common/lv_i18n.c new file mode 100644 index 0000000..ebe4126 --- /dev/null +++ b/Power_Pico/User/GUI/common/lv_i18n.c @@ -0,0 +1,229 @@ +#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_singulars[] = { + {"press here to Variable", "press here to Variable"}, + {"press here to close PD", "press here to close PD"}, + {"Screen Brightness :", "Screen Brightness :"}, + {"Enable key sound", "Enable key sound"}, + {"Enable Chinese", "Enable Chinese"}, + {"Chose Rotation", "Chose Rotation"}, + {NULL, NULL} // End mark +}; + + + +static uint8_t en_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_lang = { + .locale_name = "en", + .singulars = en_singulars, + + .locale_plural_fn = en_plural_fn +}; + +static lv_i18n_phrase_t zh_cn_singulars[] = { + {"press here to Variable", "进入步进调节"}, + {"press here to close PD", "关闭PD"}, + {"Screen Brightness :", "屏幕亮度 :"}, + {"Enable key sound", "按键声音"}, + {"Enable Chinese", "中英文切换"}, + {"Chose Rotation", "旋转角度"}, + {NULL, NULL} // End mark +}; + + + +static uint8_t zh_cn_plural_fn(int32_t num) +{ + + + + return LV_I18N_PLURAL_TYPE_OTHER; +} + +static const lv_i18n_lang_t zh_cn_lang = { + .locale_name = "zh-cn", + .singulars = zh_cn_singulars, + + .locale_plural_fn = zh_cn_plural_fn +}; + +const lv_i18n_language_pack_t lv_i18n_language_pack[] = { + &en_lang, + &zh_cn_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; +} diff --git a/Power_Pico/User/GUI/common/lv_i18n.h b/Power_Pico/User/GUI/common/lv_i18n.h new file mode 100644 index 0000000..2b5514b --- /dev/null +++ b/Power_Pico/User/GUI/common/lv_i18n.h @@ -0,0 +1,85 @@ +#ifndef LV_I18N_H +#define LV_I18N_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +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*/ \ No newline at end of file diff --git a/Power_Pico/User/GUI/ui.c b/Power_Pico/User/GUI/ui.c index 950ffbe..4795383 100644 --- a/Power_Pico/User/GUI/ui.c +++ b/Power_Pico/User/GUI/ui.c @@ -88,6 +88,10 @@ void ui_init(void) // set system settings ui_set_display_rotation(ui_get_display_rotation()); + // internationalization init + lv_i18n_init(lv_i18n_language_pack); //lv_i18n 的语言初始化 + lv_i18n_set_locale("en"); //lv_i18n 设置当前语言 + // main timer (you can add someting to do in the timer_cb if needed) lv_timer_t * ui_MainTimer = lv_timer_create(main_timer_cb, 1000, NULL); diff --git a/Power_Pico/User/GUI/ui.h b/Power_Pico/User/GUI/ui.h index ddc9ec2..ec1aedb 100644 --- a/Power_Pico/User/GUI/ui.h +++ b/Power_Pico/User/GUI/ui.h @@ -12,6 +12,7 @@ extern "C" { #include "lvgl.h" #include "./common/lv_lib_pm.h" #include "./common/lv_lib_animation.h" +#include "./common/lv_i18n.h" #include "./ui_helpers.h" void ui_init(void);