mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 21:44:39 +08:00
i18n: add session language command
This commit is contained in:
parent
0c27976763
commit
2e69283e5c
9 changed files with 68 additions and 5 deletions
|
|
@ -96,6 +96,7 @@ Ctrl+C - Exit chat
|
||||||
:search <keyword> - Search full message history (case-insensitive)
|
:search <keyword> - Search full message history (case-insensitive)
|
||||||
:mute-joins - Toggle join/leave system notifications
|
:mute-joins - Toggle join/leave system notifications
|
||||||
:support - Show quick support guide
|
:support - Show quick support guide
|
||||||
|
:lang <en|zh> - Switch UI language for this session
|
||||||
:help - Show available commands
|
:help - Show available commands
|
||||||
:clear - Clear command output
|
:clear - Clear command output
|
||||||
:q, :quit, :exit - Disconnect
|
:q, :quit, :exit - Disconnect
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
- Added a first i18n boundary: `TNT_LANG` / locale detection now chooses the
|
- Added a first i18n boundary: `TNT_LANG` / locale detection now chooses the
|
||||||
default interactive UI language (`en` or `zh`) for username prompts, status
|
default interactive UI language (`en` or `zh`) for username prompts, status
|
||||||
hints, help language, and `:support`.
|
hints, help language, and `:support`.
|
||||||
|
- Added `:lang <en|zh>` so users can switch the interactive UI language for
|
||||||
|
their current session.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- NORMAL mode now opens at the latest visible messages instead of the oldest
|
- NORMAL mode now opens at the latest visible messages instead of the oldest
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ typedef enum {
|
||||||
I18N_NORMAL_NEW_MESSAGES
|
I18N_NORMAL_NEW_MESSAGES
|
||||||
} i18n_text_id_t;
|
} i18n_text_id_t;
|
||||||
|
|
||||||
|
bool i18n_try_parse_lang(const char *value, help_lang_t *lang);
|
||||||
help_lang_t i18n_parse_lang(const char *value, help_lang_t fallback);
|
help_lang_t i18n_parse_lang(const char *value, help_lang_t fallback);
|
||||||
help_lang_t i18n_default_lang(void);
|
help_lang_t i18n_default_lang(void);
|
||||||
const char *i18n_lang_code(help_lang_t lang);
|
const char *i18n_lang_code(help_lang_t lang);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include "chat_room.h"
|
#include "chat_room.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "i18n.h"
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
#include "support.h"
|
#include "support.h"
|
||||||
#include "tui.h"
|
#include "tui.h"
|
||||||
|
|
@ -82,7 +83,8 @@ static const char *suggest_command(const char *cmd) {
|
||||||
static const char *commands[] = {
|
static const char *commands[] = {
|
||||||
"list", "users", "who", "nick", "name", "msg", "w", "inbox",
|
"list", "users", "who", "nick", "name", "msg", "w", "inbox",
|
||||||
"last", "search", "mute-joins", "mute", "support", "guide",
|
"last", "search", "mute-joins", "mute", "support", "guide",
|
||||||
"help", "commands", "clear", "cls", "q", "quit", "exit"
|
"lang", "language", "help", "commands", "clear", "cls",
|
||||||
|
"q", "quit", "exit"
|
||||||
};
|
};
|
||||||
const char *best = NULL;
|
const char *best = NULL;
|
||||||
int best_distance = 99;
|
int best_distance = 99;
|
||||||
|
|
@ -177,6 +179,7 @@ void commands_dispatch(client_t *client) {
|
||||||
"search <keyword> - Search message history\n"
|
"search <keyword> - Search message history\n"
|
||||||
"mute-joins - Toggle join/leave notices\n"
|
"mute-joins - Toggle join/leave notices\n"
|
||||||
"support - Show quick support guide\n"
|
"support - Show quick support guide\n"
|
||||||
|
"lang [en|zh] - Show or switch UI language\n"
|
||||||
"help, commands - Show this help\n"
|
"help, commands - Show this help\n"
|
||||||
"clear, cls - Clear command output\n"
|
"clear, cls - Clear command output\n"
|
||||||
"q, quit, exit - Disconnect\n"
|
"q, quit, exit - Disconnect\n"
|
||||||
|
|
@ -191,6 +194,34 @@ void commands_dispatch(client_t *client) {
|
||||||
support_append_interactive_panel(output, sizeof(output), &pos,
|
support_append_interactive_panel(output, sizeof(output), &pos,
|
||||||
client->help_lang);
|
client->help_lang);
|
||||||
|
|
||||||
|
} else if (strcmp(cmd, "lang") == 0 || strcmp(cmd, "language") == 0 ||
|
||||||
|
strncmp(cmd, "lang ", 5) == 0 ||
|
||||||
|
strncmp(cmd, "language ", 9) == 0) {
|
||||||
|
char *arg = NULL;
|
||||||
|
help_lang_t next_lang;
|
||||||
|
|
||||||
|
if (strncmp(cmd, "lang ", 5) == 0) {
|
||||||
|
arg = cmd + 5;
|
||||||
|
} else if (strncmp(cmd, "language ", 9) == 0) {
|
||||||
|
arg = cmd + 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!arg || arg[0] == '\0') {
|
||||||
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
|
"Current language: %s\n"
|
||||||
|
"Usage: lang <en|zh>\n",
|
||||||
|
i18n_lang_code(client->help_lang));
|
||||||
|
} else if (i18n_try_parse_lang(arg, &next_lang)) {
|
||||||
|
client->help_lang = next_lang;
|
||||||
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
|
"Language set to: %s\n",
|
||||||
|
i18n_lang_code(client->help_lang));
|
||||||
|
} else {
|
||||||
|
buffer_appendf(output, sizeof(output), &pos,
|
||||||
|
"Unsupported language: %s\n"
|
||||||
|
"Usage: lang <en|zh>\n", arg);
|
||||||
|
}
|
||||||
|
|
||||||
} else if (strncmp(cmd, "msg ", 4) == 0 || strncmp(cmd, "w ", 2) == 0) {
|
} else if (strncmp(cmd, "msg ", 4) == 0 || strncmp(cmd, "w ", 2) == 0) {
|
||||||
char *rest = (cmd[0] == 'w') ? cmd + 2 : cmd + 4;
|
char *rest = (cmd[0] == 'w') ? cmd + 2 : cmd + 4;
|
||||||
while (*rest == ' ') rest++;
|
while (*rest == ' ') rest++;
|
||||||
|
|
|
||||||
18
src/i18n.c
18
src/i18n.c
|
|
@ -17,23 +17,33 @@ static bool starts_with_lang(const char *value, const char *prefix) {
|
||||||
return *value == '\0' || *value == '_' || *value == '-' || *value == '.';
|
return *value == '\0' || *value == '_' || *value == '-' || *value == '.';
|
||||||
}
|
}
|
||||||
|
|
||||||
help_lang_t i18n_parse_lang(const char *value, help_lang_t fallback) {
|
bool i18n_try_parse_lang(const char *value, help_lang_t *lang) {
|
||||||
if (!value || value[0] == '\0') {
|
if (!value || value[0] == '\0') {
|
||||||
return fallback;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (starts_with_lang(value, "zh") ||
|
if (starts_with_lang(value, "zh") ||
|
||||||
starts_with_lang(value, "cn") ||
|
starts_with_lang(value, "cn") ||
|
||||||
starts_with_lang(value, "chinese")) {
|
starts_with_lang(value, "chinese")) {
|
||||||
return LANG_ZH;
|
if (lang) *lang = LANG_ZH;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (starts_with_lang(value, "en") ||
|
if (starts_with_lang(value, "en") ||
|
||||||
starts_with_lang(value, "c") ||
|
starts_with_lang(value, "c") ||
|
||||||
starts_with_lang(value, "posix")) {
|
starts_with_lang(value, "posix")) {
|
||||||
return LANG_EN;
|
if (lang) *lang = LANG_EN;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
help_lang_t i18n_parse_lang(const char *value, help_lang_t fallback) {
|
||||||
|
help_lang_t lang;
|
||||||
|
if (i18n_try_parse_lang(value, &lang)) {
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -784,6 +784,7 @@ const char* tui_get_help_text(help_lang_t lang) {
|
||||||
" :search <keyword> - Search message history\n"
|
" :search <keyword> - Search message history\n"
|
||||||
" :mute-joins - Toggle join/leave notices\n"
|
" :mute-joins - Toggle join/leave notices\n"
|
||||||
" :support - Show quick support guide\n"
|
" :support - Show quick support guide\n"
|
||||||
|
" :lang <en|zh> - Switch UI language\n"
|
||||||
" :help - Show available commands\n"
|
" :help - Show available commands\n"
|
||||||
" :clear - Clear command output\n"
|
" :clear - Clear command output\n"
|
||||||
" :q, :quit, :exit - Disconnect\n"
|
" :q, :quit, :exit - Disconnect\n"
|
||||||
|
|
@ -838,6 +839,7 @@ const char* tui_get_help_text(help_lang_t lang) {
|
||||||
" :search <关键词> - 搜索消息历史\n"
|
" :search <关键词> - 搜索消息历史\n"
|
||||||
" :mute-joins - 切换加入/离开提示\n"
|
" :mute-joins - 切换加入/离开提示\n"
|
||||||
" :support - 显示快速支持指南\n"
|
" :support - 显示快速支持指南\n"
|
||||||
|
" :lang <en|zh> - 切换界面语言\n"
|
||||||
" :help - 显示可用命令\n"
|
" :help - 显示可用命令\n"
|
||||||
" :clear - 清空命令输出\n"
|
" :clear - 清空命令输出\n"
|
||||||
" :q, :quit, :exit - 断开连接\n"
|
" :q, :quit, :exit - 断开连接\n"
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,13 @@ send -- "support\r"
|
||||||
expect "支持"
|
expect "支持"
|
||||||
expect "Press any key"
|
expect "Press any key"
|
||||||
send -- "q"
|
send -- "q"
|
||||||
|
expect "NORMAL"
|
||||||
|
send -- ":"
|
||||||
|
expect ":"
|
||||||
|
send -- "lang en\r"
|
||||||
|
expect "Language set to: en"
|
||||||
|
expect "Press any key"
|
||||||
|
send -- "q"
|
||||||
sleep 0.2
|
sleep 0.2
|
||||||
send -- "\003"
|
send -- "\003"
|
||||||
sleep 0.2
|
sleep 0.2
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,19 @@
|
||||||
static int tests_passed = 0;
|
static int tests_passed = 0;
|
||||||
|
|
||||||
TEST(parse_explicit_languages) {
|
TEST(parse_explicit_languages) {
|
||||||
|
help_lang_t lang;
|
||||||
|
|
||||||
assert(i18n_parse_lang("zh", LANG_EN) == LANG_ZH);
|
assert(i18n_parse_lang("zh", LANG_EN) == LANG_ZH);
|
||||||
assert(i18n_parse_lang("zh_CN.UTF-8", LANG_EN) == LANG_ZH);
|
assert(i18n_parse_lang("zh_CN.UTF-8", LANG_EN) == LANG_ZH);
|
||||||
assert(i18n_parse_lang("cn", LANG_EN) == LANG_ZH);
|
assert(i18n_parse_lang("cn", LANG_EN) == LANG_ZH);
|
||||||
assert(i18n_parse_lang("en", LANG_ZH) == LANG_EN);
|
assert(i18n_parse_lang("en", LANG_ZH) == LANG_EN);
|
||||||
assert(i18n_parse_lang("en_US.UTF-8", LANG_ZH) == LANG_EN);
|
assert(i18n_parse_lang("en_US.UTF-8", LANG_ZH) == LANG_EN);
|
||||||
|
|
||||||
|
assert(i18n_try_parse_lang("zh", &lang) == true);
|
||||||
|
assert(lang == LANG_ZH);
|
||||||
|
assert(i18n_try_parse_lang("en", &lang) == true);
|
||||||
|
assert(lang == LANG_EN);
|
||||||
|
assert(i18n_try_parse_lang("fr", &lang) == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(parse_unknown_uses_fallback) {
|
TEST(parse_unknown_uses_fallback) {
|
||||||
|
|
|
||||||
1
tnt.1
1
tnt.1
|
|
@ -121,6 +121,7 @@ l l.
|
||||||
:search \fIkeyword\fR Case\-insensitive search across full message history
|
:search \fIkeyword\fR Case\-insensitive search across full message history
|
||||||
:mute\-joins Toggle join/leave system notifications on/off
|
:mute\-joins Toggle join/leave system notifications on/off
|
||||||
:support Show quick support guide
|
:support Show quick support guide
|
||||||
|
:lang \fIen|zh\fR Switch UI language for this session
|
||||||
:help Show available commands
|
:help Show available commands
|
||||||
:clear Clear command output
|
:clear Clear command output
|
||||||
:q, :quit, :exit Disconnect
|
:q, :quit, :exit Disconnect
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue