feat(i18n): 添加多语言支持功能

- 新增国际化系统,支持中英文切换
- 添加语言选择对话框和语言切换按钮回调
- 扩展配置系统以支持语言设置存储
- 创建语言文件目录结构和占位文件
- 更新主窗口支持UI文本动态刷新
This commit is contained in:
2026-03-26 20:44:22 +08:00
parent 9a78b88c4a
commit 4fe7dc47e4
2343 changed files with 127697 additions and 9 deletions
+11
View File
@@ -9,8 +9,10 @@
#include "utils/error_code.h"
#include "utils/safe_string.h"
#include "utils/logger.h"
#include "utils/i18n.h"
#include "ui/ui_utils.h"
#include "ui/dialogs.h"
#include "ui/main_window.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
@@ -574,6 +576,15 @@ void load_all_paths(void)
IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "loaded"));
}
// 按钮回调:语言切换
int btn_lang_cb(Ihandle *self)
{
Ihandle *dlg = IupGetDialog(self);
language_select_dialog();
refresh_main_window_ui(dlg);
return IUP_DEFAULT;
}
// 按钮回调:帮助
int btn_help_cb(Ihandle *self)
{
+35
View File
@@ -244,4 +244,39 @@ int lua_config_reload(void)
int lua_config_is_loaded(void)
{
return G_loaded;
}
int lua_config_set_string(const char *section, const char *key, const char *value)
{
if (section == NULL || key == NULL || value == NULL)
{
return -1;
}
if (G_L == NULL)
{
return -1;
}
lua_getglobal(G_L, "config");
if (!lua_istable(G_L, -1))
{
lua_settop(G_L, 0);
return -1;
}
lua_getfield(G_L, -1, section);
if (!lua_istable(G_L, -1))
{
lua_pop(G_L, 1);
lua_createtable(G_L, 0, 4);
lua_setfield(G_L, -2, section);
lua_getfield(G_L, -1, section);
}
lua_pushstring(G_L, value);
lua_setfield(G_L, -2, key);
lua_settop(G_L, 0);
return 0;
}
+9 -4
View File
@@ -3,11 +3,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <libintl.h>
#include "core/app_context.h"
#include "core/lua_config.h"
#include "utils/string_ext.h"
#include "utils/os_env.h"
#include "utils/logger.h"
#include "utils/i18n.h"
#include "controller/callbacks.h"
#include "ui/main_window.h"
@@ -46,11 +48,14 @@ int main(int argc, char **argv)
if (lua_config_init() != 0)
{
IupMessage("警告", "Lua 配置系统初始化失败,将使用默认值");
IupMessage(_("Warning"), "Lua 配置系统初始化失败,将使用默认值");
}
log_info("Lua config initialized");
// 初始化国际化系统
i18n_init(NULL);
// 在管理员模式下,解决无法拖拽文件到列表框的问题 (UIPI)
// 需要加载 User32.dll 获取 ChangeWindowMessageFilter 函数
HMODULE hUser32 = LoadLibraryW(L"user32.dll");
@@ -76,7 +81,7 @@ int main(int argc, char **argv)
AppContext *ctx = create_app_context();
if (!ctx)
{
IupMessage("错误", "无法分配内存创建应用上下文");
IupMessage(_("Error"), "无法分配内存创建应用上下文");
IupClose();
return 1;
}
@@ -91,11 +96,11 @@ int main(int argc, char **argv)
// 检查管理员权限
if (!check_admin())
{
IupMessage("警告", lua_config_get_string("status", "admin_warning"));
IupMessage(_("Warning"), _(lua_config_get_string("status", "admin_warning")));
Ihandle *lbl_status = IupGetDialogChild(dlg, "LBL_STATUS");
if (lbl_status)
IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "readonly"));
IupSetAttribute(lbl_status, "TITLE", _(lua_config_get_string("status", "readonly")));
Ihandle *btn_new = IupGetDialogChild(dlg, "BTN_NEW");
Ihandle *btn_edit = IupGetDialogChild(dlg, "BTN_EDIT");
+59 -3
View File
@@ -1,6 +1,7 @@
#include "ui/dialogs.h"
#include "core/lua_config.h"
#include "utils/safe_string.h"
#include "utils/i18n.h"
#include <iup.h>
#include <string.h>
@@ -26,7 +27,7 @@ int custom_input_dialog(const char *title, const char *label_text, char *buffer,
Ihandle *text = IupText(NULL);
IupSetAttribute(text, "VALUE", buffer);
IupSetAttribute(text, "EXPAND", "HORIZONTAL");
IupSetAttribute(text, "RASTERSIZE", "500x");
IupSetAttribute(text, "RASTERSIZE", lua_config_get_string("input_dialog", "text_size"));
IupSetAttribute(text, "NAME", "INPUT_TEXT");
Ihandle *btn_ok = IupButton(lua_config_get_string("button", "ok"), NULL);
@@ -42,8 +43,8 @@ int custom_input_dialog(const char *title, const char *label_text, char *buffer,
text,
IupHbox(IupFill(), btn_ok, btn_cancel, NULL),
NULL);
IupSetAttribute(vbox, "MARGIN", "15x15");
IupSetAttribute(vbox, "GAP", "10");
IupSetAttribute(vbox, "MARGIN", lua_config_get_string("input_dialog", "margin"));
IupSetAttribute(vbox, "GAP", lua_config_get_string("input_dialog", "gap"));
Ihandle *dlg = IupDialog(vbox);
IupSetAttribute(dlg, "TITLE", title);
@@ -66,6 +67,61 @@ int custom_input_dialog(const char *title, const char *label_text, char *buffer,
}
}
IupDestroy(dlg);
return result;
}
// 语言选择对话框
int language_select_dialog(void)
{
const char *current_lang = i18n_get_current_language();
Ihandle *list = IupList(NULL);
IupSetAttribute(list, "NAME", "LANG_LIST");
IupSetAttribute(list, "DROPDOWN", "YES");
IupSetAttribute(list, "VALUE", (strcmp(current_lang, "zh_CN") == 0) ? "1" : "2");
IupSetAttribute(list, "1", _("Chinese (Simplified)"));
IupSetAttribute(list, "2", _("English"));
IupSetAttribute(list, "RASTERSIZE", lua_config_get_string("language", "list_size"));
Ihandle *btn_ok = IupButton(_("OK"), NULL);
IupSetCallback(btn_ok, "ACTION", on_dialog_ok);
IupSetAttribute(btn_ok, "RASTERSIZE", lua_config_get_string("button", "rastersize"));
Ihandle *btn_cancel = IupButton(_("Cancel"), NULL);
IupSetCallback(btn_cancel, "ACTION", on_dialog_cancel);
IupSetAttribute(btn_cancel, "RASTERSIZE", lua_config_get_string("button", "rastersize"));
Ihandle *vbox = IupVbox(
IupLabel(_("Language")),
list,
IupHbox(IupFill(), btn_ok, btn_cancel, NULL),
NULL);
IupSetAttribute(vbox, "MARGIN", lua_config_get_string("language", "margin"));
IupSetAttribute(vbox, "GAP", lua_config_get_string("language", "gap"));
Ihandle *dlg = IupDialog(vbox);
IupSetAttribute(dlg, "TITLE", _("Language"));
IupSetAttribute(dlg, "MINBOX", "NO");
IupSetAttribute(dlg, "MAXBOX", "NO");
IupSetAttribute(dlg, "RESIZE", "NO");
IupSetAttribute(dlg, "RASTERSIZE", lua_config_get_string("language", "dialog_size"));
IupSetAttributeHandle(dlg, "DEFAULTENTER", btn_ok);
IupSetAttributeHandle(dlg, "DEFAULTESC", btn_cancel);
IupPopup(dlg, IUP_CENTER, IUP_CENTER);
int result = IupGetInt(dlg, "MY_STATUS");
if (result == 1)
{
int selected = IupGetInt(list, "VALUE");
if (selected == 1)
i18n_change_language("zh_CN");
else
i18n_change_language("en_US");
}
IupDestroy(dlg);
return result;
}
+72 -1
View File
@@ -1,7 +1,9 @@
#include "ui/main_window.h"
#include "controller/callbacks.h"
#include "core/lua_config.h"
#include "utils/i18n.h"
#include <stddef.h>
#include <string.h>
// 创建路径列表控件
static Ihandle *create_path_list(const char *name)
@@ -66,6 +68,11 @@ Ihandle *create_main_window(void)
Ihandle *btn_export = IupButton(lua_config_get_string("button", "export"), NULL);
IupSetAttribute(btn_export, "NAME", "BTN_EXPORT");
// 创建语言切换按钮
Ihandle *btn_lang = IupButton(_("Language"), NULL);
IupSetAttribute(btn_lang, "NAME", "BTN_LANG");
IupSetCallback(btn_lang, "ACTION", (Icallback)btn_lang_cb);
// 设置按钮回调
IupSetCallback(btn_new, "ACTION", (Icallback)btn_new_cb);
IupSetCallback(btn_edit, "ACTION", (Icallback)btn_edit_cb);
@@ -88,6 +95,7 @@ Ihandle *create_main_window(void)
IupSetAttribute(btn_clean, "RASTERSIZE", btn_size);
IupSetAttribute(btn_import, "RASTERSIZE", btn_size);
IupSetAttribute(btn_export, "RASTERSIZE", btn_size);
IupSetAttribute(btn_lang, "RASTERSIZE", btn_size);
// 创建操作按钮垂直布局
Ihandle *vbox_btns = IupVbox(
@@ -130,7 +138,7 @@ Ihandle *create_main_window(void)
IupSetAttribute(btn_help, "RASTERSIZE", btn_size);
// 创建底部按钮水平布局
Ihandle *hbox_bottom = IupHbox(lbl_status, IupFill(), btn_help, btn_ok, btn_cancel, NULL);
Ihandle *hbox_bottom = IupHbox(lbl_status, IupFill(), btn_help, btn_lang, btn_ok, btn_cancel, NULL);
IupSetAttribute(hbox_bottom, "GAP", lua_config_get_string("layout", "hbox_gap"));
IupSetAttribute(hbox_bottom, "MARGIN", lua_config_get_string("layout", "hbox_margin"));
IupSetAttribute(hbox_bottom, "ALIGNMENT", lua_config_get_string("layout", "hbox_alignment"));
@@ -154,4 +162,67 @@ Ihandle *create_main_window(void)
IupSetAttribute(dlg, "MAXBOX", "NO");
return dlg;
}
void refresh_main_window_ui(Ihandle *main_dlg)
{
if (!main_dlg)
return;
IupSetAttribute(main_dlg, "TITLE", lua_config_get_string("app", "name"));
IupSetAttribute(main_dlg, "TABTITLE0", lua_config_get_string("label", "tab_sys"));
IupSetAttribute(main_dlg, "TABTITLE1", lua_config_get_string("label", "tab_user"));
Ihandle *btn = IupGetDialogChild(main_dlg, "BTN_NEW");
if (btn)
IupSetAttribute(btn, "TITLE", lua_config_get_string("button", "new"));
btn = IupGetDialogChild(main_dlg, "BTN_EDIT");
if (btn)
IupSetAttribute(btn, "TITLE", lua_config_get_string("button", "edit"));
btn = IupGetDialogChild(main_dlg, "BTN_BROWSE");
if (btn)
IupSetAttribute(btn, "TITLE", lua_config_get_string("button", "browse"));
btn = IupGetDialogChild(main_dlg, "BTN_DEL");
if (btn)
IupSetAttribute(btn, "TITLE", lua_config_get_string("button", "del"));
btn = IupGetDialogChild(main_dlg, "BTN_UP");
if (btn)
IupSetAttribute(btn, "TITLE", lua_config_get_string("button", "up"));
btn = IupGetDialogChild(main_dlg, "BTN_DOWN");
if (btn)
IupSetAttribute(btn, "TITLE", lua_config_get_string("button", "down"));
btn = IupGetDialogChild(main_dlg, "BTN_CLEAN");
if (btn)
IupSetAttribute(btn, "TITLE", lua_config_get_string("button", "clean"));
btn = IupGetDialogChild(main_dlg, "BTN_IMPORT");
if (btn)
IupSetAttribute(btn, "TITLE", lua_config_get_string("button", "import"));
btn = IupGetDialogChild(main_dlg, "BTN_EXPORT");
if (btn)
IupSetAttribute(btn, "TITLE", lua_config_get_string("button", "export"));
btn = IupGetDialogChild(main_dlg, "BTN_LANG");
if (btn)
IupSetAttribute(btn, "TITLE", _("Language"));
btn = IupGetDialogChild(main_dlg, "BTN_OK");
if (btn)
IupSetAttribute(btn, "TITLE", lua_config_get_string("button", "ok"));
btn = IupGetDialogChild(main_dlg, "BTN_CANCEL");
if (btn)
IupSetAttribute(btn, "TITLE", lua_config_get_string("button", "cancel"));
btn = IupGetDialogChild(main_dlg, "BTN_HELP");
if (btn)
IupSetAttribute(btn, "TITLE", lua_config_get_string("button", "help"));
}
+112
View File
@@ -0,0 +1,112 @@
#include "utils/i18n.h"
#include "utils/logger.h"
#include "core/lua_config.h"
#include <windows.h>
#include <stdio.h>
#include <string.h>
static char current_language[16] = {0};
static char locale_path[256] = {0};
static void load_saved_language(void)
{
FILE *fp = fopen("language.txt", "r");
if (fp != NULL)
{
char lang[16] = {0};
if (fgets(lang, sizeof(lang), fp) != NULL)
{
int len = strlen(lang);
if (len > 0 && lang[len - 1] == '\n')
lang[len - 1] = '\0';
if (strlen(lang) > 0)
{
size_t copy_len = sizeof(current_language) - 1;
if (strlen(lang) < copy_len)
copy_len = strlen(lang);
memcpy(current_language, lang, copy_len);
current_language[copy_len] = '\0';
log_info("Loaded saved language: %s", current_language);
fclose(fp);
return;
}
}
fclose(fp);
}
}
void i18n_init(const char *default_lang)
{
setlocale(LC_ALL, "");
snprintf(locale_path, sizeof(locale_path), "./locale");
bindtextdomain("messages", locale_path);
textdomain("messages");
load_saved_language();
if (strlen(current_language) == 0)
{
const char *detected = i18n_detect_system_language();
size_t copy_len = sizeof(current_language) - 1;
size_t src_len = strlen(detected);
if (src_len < copy_len)
copy_len = src_len;
memcpy(current_language, detected, copy_len);
current_language[copy_len] = '\0';
}
char mo_path[512];
snprintf(mo_path, sizeof(mo_path), "%s/%s/LC_MESSAGES/%s.mo", locale_path, current_language, current_language);
bindtextdomain("messages", mo_path);
log_info("i18n initialized with language: %s", current_language);
}
const char *i18n_detect_system_language(void)
{
LANGID langId = GetUserDefaultUILanguage();
log_info("Detected system language ID: 0x%04X", langId);
if (langId == 0x0804)
{
return "zh_CN";
}
return "en_US";
}
void i18n_change_language(const char *lang)
{
if (!lang || strlen(lang) == 0)
return;
if (strcmp(current_language, lang) == 0)
return;
size_t copy_len = sizeof(current_language) - 1;
size_t src_len = strlen(lang);
if (src_len < copy_len)
copy_len = src_len;
memcpy(current_language, lang, copy_len);
current_language[copy_len] = '\0';
char new_lang_path[512];
snprintf(new_lang_path, sizeof(new_lang_path), "%s/%s/LC_MESSAGES/%s.mo", locale_path, lang, lang);
bindtextdomain("messages", new_lang_path);
FILE *fp = fopen("language.txt", "w");
if (fp != NULL)
{
fprintf(fp, "%s\n", lang);
fclose(fp);
}
log_info("Language changed to: %s", lang);
}
const char *i18n_get_current_language(void)
{
return current_language;
}