mirror of
https://github.com/zlgopen/awtk.git
synced 2025-05-08 11:33:48 +08:00
add locale_info_xml
This commit is contained in:
parent
fdbf807ddb
commit
2e5b0291bc
@ -1,5 +1,8 @@
|
||||
# 最新动态
|
||||
|
||||
2025/04/09
|
||||
* 添加可从xml中解析本地化信息的locale_info,完善预览程序可从strings.xml获取翻译文本(感谢培煌提供补丁)
|
||||
|
||||
2025/04/08
|
||||
* 增加 rectf_intersect (感谢智明提供补丁)
|
||||
* 修复nanovg的脏矩形获取不正常的问题(感谢智明提供补丁)
|
||||
|
@ -55,6 +55,7 @@
|
||||
#include "base/lcd_profile.h"
|
||||
#include "base/line_break.h"
|
||||
#include "base/locale_info.h"
|
||||
#include "base/locale_info_xml.h"
|
||||
#include "base/main_loop.h"
|
||||
#include "base/pixel.h"
|
||||
#include "base/pixel_pack_unpack.h"
|
||||
|
223
src/base/locale_info_xml.c
Normal file
223
src/base/locale_info_xml.c
Normal file
@ -0,0 +1,223 @@
|
||||
/**
|
||||
* File: locale_info_xml.c
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: locale_info_xml
|
||||
*
|
||||
* Copyright (c) 2018 - 2025 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2025-04-08 Li PeiHuang <lipeihuang@zlg.cn> created
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tkc/fs.h"
|
||||
#include "base/locale_info_xml.h"
|
||||
#include "base/assets_manager.h"
|
||||
#include "base/locale_info.h"
|
||||
|
||||
static const char* locale_info_xml_get_language_str(locale_info_t* locale_info) {
|
||||
locale_info_xml_t* xml_info = (locale_info_xml_t*)locale_info;
|
||||
if (xml_info == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
str_clear(xml_info->language);
|
||||
str_set(xml_info->language, locale_info->language);
|
||||
str_append(xml_info->language, "_");
|
||||
str_append(xml_info->language, locale_info->country);
|
||||
|
||||
return xml_info->language->str;
|
||||
}
|
||||
|
||||
static int32_t locale_info_xml_get_node_size(locale_info_xml_t* xml_info, conf_node_t* node) {
|
||||
if (node == NULL) { // node == NULL, 则获取root节点的size
|
||||
return conf_doc_get_int(xml_info->doc, "root.#size", -1);
|
||||
} else {
|
||||
return conf_node_count_children(node);
|
||||
}
|
||||
}
|
||||
|
||||
static conf_node_t* locale_info_xml_get_node_by_text(locale_info_t* locale_info, const char* text) {
|
||||
value_t v;
|
||||
char path[MAX_PATH + 1] = {0};
|
||||
locale_info_xml_t* xml_info = (locale_info_xml_t*)locale_info;
|
||||
int32_t size = locale_info_xml_get_node_size(xml_info, NULL);
|
||||
|
||||
for (int32_t i = 0; i < size; i++) {
|
||||
memset(path, 0, sizeof(path));
|
||||
tk_snprintf(path, sizeof(path), "root.[%d].name", i);
|
||||
|
||||
conf_node_t* name_node = conf_doc_find_node(xml_info->doc, NULL, path, FALSE);
|
||||
if (conf_node_get_value(name_node, &v) != RET_OK) {
|
||||
continue;
|
||||
}
|
||||
const char* name = value_str(&v);
|
||||
|
||||
if (tk_str_eq(name, text)) {
|
||||
return name_node->parent;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static conf_node_t* locale_info_xml_get_node_by_language(locale_info_t* locale_info,
|
||||
conf_node_t* node, const char* language) {
|
||||
value_t v;
|
||||
char path[MAX_PATH + 1] = {0};
|
||||
locale_info_xml_t* xml_info = (locale_info_xml_t*)locale_info;
|
||||
int32_t size = locale_info_xml_get_node_size(xml_info, node);
|
||||
|
||||
for (int32_t i = 0; i < size; i++) {
|
||||
memset(path, 0, sizeof(path));
|
||||
tk_snprintf(path, sizeof(path), "[%d].name", i);
|
||||
conf_node_t* name_node = conf_doc_find_node(xml_info->doc, node, path, FALSE);
|
||||
if (conf_node_get_value(name_node, &v) != RET_OK) {
|
||||
continue;
|
||||
}
|
||||
const char* name = value_str(&v);
|
||||
|
||||
if (tk_str_eq(name, language)) {
|
||||
return name_node->parent;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char* locale_info_xml_get_node_text(locale_info_t* locale_info, conf_node_t* node) {
|
||||
locale_info_xml_t* xml_info = (locale_info_xml_t*)locale_info;
|
||||
conf_node_t* text_node = NULL;
|
||||
|
||||
if (node == NULL) {
|
||||
return conf_doc_get_str(xml_info->doc, "root.@text", NULL);
|
||||
} else {
|
||||
text_node = conf_doc_find_node(xml_info->doc, node, "@text", FALSE);
|
||||
if (conf_node_get_value(text_node, xml_info->text_value) == RET_OK) {
|
||||
return value_str(xml_info->text_value);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char* locale_info_xml_get_tr(void* ctx, const char* text) {
|
||||
locale_info_t* info = (locale_info_t*)ctx;
|
||||
if (info == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* lang = locale_info_xml_get_language_str(info);
|
||||
conf_node_t* t_node = locale_info_xml_get_node_by_text(info, text);
|
||||
if (t_node != NULL) {
|
||||
conf_node_t* l_node = locale_info_xml_get_node_by_language(info, t_node, lang);
|
||||
if (l_node != NULL) {
|
||||
return locale_info_xml_get_node_text(info, l_node);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
locale_info_t* locale_info_xml_create(const char* language, const char* country) {
|
||||
locale_info_t* info = locale_info_create(language, country);
|
||||
locale_info_xml_t* xml_info = TKMEM_ZALLOC(locale_info_xml_t);
|
||||
memcpy(xml_info, info, sizeof(locale_info_t));
|
||||
TKMEM_FREE(info);
|
||||
|
||||
xml_info->doc = NULL;
|
||||
xml_info->strings_url = str_create(0);
|
||||
xml_info->language = str_create(0);
|
||||
xml_info->text_value = value_create();
|
||||
|
||||
info = (locale_info_t*)xml_info;
|
||||
locale_info_set_fallback_tr2(info, locale_info_xml_get_tr, info);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
static str_t* get_strings_content(const char* path, str_t* result) {
|
||||
if (fs_file_exist(os_fs(), path)) {
|
||||
uint32_t size;
|
||||
char* content = (char*)file_read(path, &size);
|
||||
if (content != NULL) {
|
||||
str_set(result, "<root>");
|
||||
str_append(result, content);
|
||||
str_append(result, "</root>");
|
||||
|
||||
TKMEM_FREE(content);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ret_t locale_info_xml_reload(locale_info_xml_t* xml_info) {
|
||||
if (xml_info == NULL) {
|
||||
return RET_BAD_PARAMS;
|
||||
}
|
||||
|
||||
if (xml_info->doc != NULL) {
|
||||
conf_doc_destroy(xml_info->doc);
|
||||
}
|
||||
|
||||
str_t* content = str_create(0);
|
||||
content = get_strings_content(xml_info->strings_url->str, content);
|
||||
|
||||
xml_info->doc = conf_doc_load_xml(content->str);
|
||||
str_destroy(content);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t locale_info_xml_set_url(locale_info_t* locale_info, const char* strings_url) {
|
||||
locale_info_xml_t* xml_info = (locale_info_xml_t*)locale_info;
|
||||
if (xml_info == NULL) {
|
||||
return RET_BAD_PARAMS;
|
||||
}
|
||||
|
||||
str_set(xml_info->strings_url, strings_url);
|
||||
locale_info_xml_reload(xml_info);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
ret_t locale_info_xml_set_assets_manager(locale_info_t* locale_info,
|
||||
assets_manager_t* assets_manager) {
|
||||
ret_t ret = locale_info_set_assets_manager(locale_info, assets_manager);
|
||||
if (ret != RET_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char* res_root = assets_manager_get_res_root(assets_manager);
|
||||
const char* theme = assets_manager_get_theme_name(assets_manager);
|
||||
|
||||
if (res_root != NULL && theme != NULL) {
|
||||
char path[MAX_PATH + 1] = {0};
|
||||
tk_snprintf(path, sizeof(path), "%s/%s/strings/strings.xml", res_root, theme);
|
||||
locale_info_xml_set_url(locale_info, path);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret_t locale_info_xml_destroy(locale_info_t* locale_info) {
|
||||
locale_info_xml_t* xml_info = (locale_info_xml_t*)locale_info;
|
||||
|
||||
conf_doc_destroy(xml_info->doc);
|
||||
str_destroy(xml_info->strings_url);
|
||||
str_destroy(xml_info->language);
|
||||
value_destroy(xml_info->text_value);
|
||||
locale_info_destroy(locale_info);
|
||||
|
||||
return RET_OK;
|
||||
}
|
94
src/base/locale_info_xml.h
Normal file
94
src/base/locale_info_xml.h
Normal file
@ -0,0 +1,94 @@
|
||||
/**
|
||||
* File: locale_info_xml.h
|
||||
* Author: AWTK Develop Team
|
||||
* Brief: locale_info_xml
|
||||
*
|
||||
* Copyright (c) 2018 - 2025 Guangzhou ZHIYUAN Electronics Co.,Ltd.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* License file for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* History:
|
||||
* ================================================================
|
||||
* 2025-04-08 Li PeiHuang <lipeihuang@zlg.cn> created
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TK_LOCALE_INFO_XML_H
|
||||
#define TK_LOCALE_INFO_XML_H
|
||||
|
||||
#include "base/locale_info.h"
|
||||
#include "conf_io/conf_xml.h"
|
||||
|
||||
BEGIN_C_DECLS
|
||||
|
||||
/**
|
||||
* @class locale_info_xml_t
|
||||
* @parent locale_info_t
|
||||
* @annotation ["scriptable"]
|
||||
* 本地化信息。
|
||||
* locale_info_t 的子类。
|
||||
* 提供从 xml 文件中获取本地化信息的功能。
|
||||
*
|
||||
* 注意:fallback_tr2 回调已被设置用于从xml文件中获取本地化信息,不可再重复设置,否则将导致功能失效!
|
||||
*
|
||||
*/
|
||||
typedef struct _locale_info_xml_t {
|
||||
locale_info_t info;
|
||||
|
||||
/*private*/
|
||||
conf_doc_t* doc;
|
||||
str_t* strings_url;
|
||||
str_t* language;
|
||||
value_t* text_value;
|
||||
} locale_info_xml_t;
|
||||
|
||||
/**
|
||||
* @method locale_info_xml_create
|
||||
* 创建locale_info_xml。
|
||||
* @annotation ["constructor"]
|
||||
* @param {const char*} language 语言。
|
||||
* @param {const char*} country 国家或地区。
|
||||
*
|
||||
* @return {locale_info_t*} 返回locale_info对象。
|
||||
*/
|
||||
locale_info_t* locale_info_xml_create(const char* language, const char* country);
|
||||
|
||||
/**
|
||||
* @method locale_info_xml_set_url
|
||||
* 设置本地化文件路径。
|
||||
* @param {locale_info_t*} locale_info locale_info 对象。
|
||||
* @param {const char*} strings_url 本地化文件路径。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t locale_info_xml_set_url(locale_info_t* locale_info, const char* strings_url);
|
||||
|
||||
/**
|
||||
* @method locale_info_xml_set_assets_manager
|
||||
* 设置资源管理器对象。
|
||||
* @param {locale_info_t*} locale_info locale_info 对象。
|
||||
* @param {assets_manager_t*} assets_manager 资源管理器。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t locale_info_xml_set_assets_manager(locale_info_t* locale_info,
|
||||
assets_manager_t* assets_manager);
|
||||
|
||||
/**
|
||||
* @method locale_info_xml_destroy
|
||||
* 释放全部资源并销毁 locale_info_xml 对象。
|
||||
* @param {locale_info_t*} locale_info locale_info 对象。
|
||||
*
|
||||
* @return {ret_t} 返回RET_OK表示成功,否则表示失败。
|
||||
*/
|
||||
ret_t locale_info_xml_destroy(locale_info_t* locale_info);
|
||||
|
||||
END_C_DECLS
|
||||
|
||||
#endif /*TK_LOCALE_INFO_XML_H*/
|
@ -1,4 +1,6 @@
|
||||
#include "base/locale_info.h"
|
||||
#include "base/locale_info_xml.h"
|
||||
#include "tkc/fs.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "common.h"
|
||||
@ -48,3 +50,37 @@ TEST(Locale, basic) {
|
||||
|
||||
locale_info_destroy(locale_info);
|
||||
}
|
||||
|
||||
static ret_t prepare_test_file(const char* file_name) {
|
||||
const char* content =
|
||||
"<string name=\"test\">\n<language name=\"en_US\">TEST</language>\n"
|
||||
"<language name=\"zh_CN\">测试</language>\n</string>\n"
|
||||
"<string name=\"cn\">\n<language name=\"en_US\">CN</language>\n"
|
||||
"<language name=\"zh_CN\">中文</language>\n</string>\n"
|
||||
"<string name=\"en\">\n<language name=\"en_US\">EN</language>\n"
|
||||
"<language name=\"zh_CN\">英文</language>\n</string>\n";
|
||||
|
||||
return file_write(file_name, content, strlen(content));
|
||||
}
|
||||
|
||||
TEST(Locale, xml_basic) {
|
||||
const char* file_name = "locale_info_xml_test.xml";
|
||||
prepare_test_file(file_name);
|
||||
|
||||
const char* str = "test";
|
||||
uint32_t id = 0;
|
||||
locale_info_t* locale_info = locale_info_xml_create("en", "US");
|
||||
locale_info_xml_set_url(locale_info, file_name);
|
||||
|
||||
ASSERT_EQ(string("TEST"), string(locale_info_tr(locale_info, str)));
|
||||
locale_info_change(locale_info, "zh", "CN");
|
||||
assert_str_eq(L"测试", locale_info_tr(locale_info, str));
|
||||
|
||||
str = "cn";
|
||||
assert_str_eq(L"中文", locale_info_tr(locale_info, str));
|
||||
locale_info_change(locale_info, "en", "US");
|
||||
ASSERT_EQ(string("CN"), string(locale_info_tr(locale_info, str)));
|
||||
|
||||
locale_info_xml_destroy(locale_info);
|
||||
file_remove(file_name);
|
||||
}
|
||||
|
@ -24,9 +24,7 @@
|
||||
#endif
|
||||
|
||||
#include "awtk.h"
|
||||
#include "tkc/dl.h"
|
||||
#include "tkc/log.h"
|
||||
#include "base/timer.h"
|
||||
#include "base/locale_info_xml.h"
|
||||
|
||||
#include "ui_loader/ui_loader_xml.h"
|
||||
#include "ui_loader/ui_loader_default.h"
|
||||
@ -51,6 +49,7 @@ static const char* s_theme = NULL;
|
||||
static const char* s_log_level = NULL;
|
||||
static const char* s_fps = NULL;
|
||||
static bool_t s_enable_std_font = FALSE;
|
||||
static locale_info_t* s_old_locale_info = NULL;
|
||||
|
||||
#undef APP_RES_ROOT // 以便可以通过命令行参数指定res目录
|
||||
|
||||
@ -251,18 +250,33 @@ static ret_t refresh_in_timer(const timer_info_t* info) {
|
||||
return RET_REPEAT;
|
||||
}
|
||||
|
||||
static ret_t get_res_strings_file_path(char* result, size_t size) {
|
||||
return path_build(result, size, s_res_root, s_theme, "strings", "strings.xml", NULL);
|
||||
}
|
||||
|
||||
static ret_t application_on_launch(void) {
|
||||
// 当程序初始化完成时调用,全局只触发一次。
|
||||
|
||||
if (s_language != NULL && *s_language != '\0') {
|
||||
const char* p = strstr(s_language, "_");
|
||||
if (p != NULL) {
|
||||
char language[TK_NAME_LEN + 1] = {0};
|
||||
const char* country = NULL;
|
||||
const char* p = strstr(s_language, "_");
|
||||
|
||||
if (p != NULL) {
|
||||
tk_strncpy(language, s_language, tk_min((size_t)(p - s_language), sizeof(language)));
|
||||
locale_info_change(locale_info(), language, p + 1);
|
||||
country = (p + 1);
|
||||
} else {
|
||||
locale_info_change(locale_info(), s_language, "");
|
||||
country = "";
|
||||
}
|
||||
|
||||
s_old_locale_info = locale_info();
|
||||
locale_info_t* info = locale_info_xml_create(language, country);
|
||||
locale_info_set(info);
|
||||
assets_manager_t* am = s_old_locale_info->assets_manager != NULL
|
||||
? s_old_locale_info->assets_manager
|
||||
: assets_manager();
|
||||
locale_info_xml_set_assets_manager(info, am);
|
||||
locale_info_change(info, s_language, country);
|
||||
}
|
||||
|
||||
widget_set_style_str(window_manager(), "bg_color", "white");
|
||||
@ -273,6 +287,9 @@ static ret_t application_on_launch(void) {
|
||||
|
||||
static ret_t application_on_exit(void) {
|
||||
// 当程序退出时调用,全局只触发一次。
|
||||
locale_info_t* info = locale_info();
|
||||
locale_info_set(s_old_locale_info);
|
||||
locale_info_xml_destroy(info);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
@ -338,6 +355,7 @@ static ret_t application_init(void) {
|
||||
if (s_system_bar_bottom != NULL && *s_system_bar_bottom != '\0') {
|
||||
window_open(s_system_bar_bottom);
|
||||
}
|
||||
|
||||
preview_ui(s_ui);
|
||||
|
||||
return RET_OK;
|
||||
@ -384,9 +402,12 @@ ret_t assets_init(void) {
|
||||
|
||||
res_root = assets_manager_get_res_root(am);
|
||||
tk_snprintf(path, sizeof(path), "%s/assets/default/raw/ui", res_root);
|
||||
if (!dir_exist(path)) {
|
||||
tk_snprintf(path, sizeof(path), "%s/default/ui", res_root);
|
||||
if (!dir_exist(path)) {
|
||||
run_default = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (run_default) {
|
||||
char path[MAX_PATH + 1];
|
||||
|
Loading…
x
Reference in New Issue
Block a user