mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 04:34:38 +08:00
i18n: rename help language state to ui language
This commit is contained in:
parent
1f1c2398b6
commit
06a10e2df8
30 changed files with 216 additions and 210 deletions
|
|
@ -3,6 +3,9 @@
|
|||
## Unreleased
|
||||
|
||||
### Changed
|
||||
- Renamed the internal language state from help-oriented names to
|
||||
UI-language names (`ui_lang_t`, `client->ui_lang`, and
|
||||
`i18n_*_ui_lang`) so future i18n work has a correctly named seam.
|
||||
- Command names, aliases, help summaries, concise-manual command rows, and
|
||||
unknown-command suggestions now share a dedicated `command_catalog` module.
|
||||
- COMMAND-mode output is now a small scrollable pager with `j/k`, page
|
||||
|
|
@ -28,7 +31,7 @@
|
|||
### Added
|
||||
- Added a first i18n boundary: `TNT_LANG` / locale detection now chooses the
|
||||
default interactive UI language (`en` or `zh`) for username prompts, status
|
||||
hints, help language, and `:support`.
|
||||
hints, help output, and `:support`.
|
||||
- Added `:lang <en|zh>` so users can switch the interactive UI language for
|
||||
their current session.
|
||||
- COMMAND-mode `:help`, unknown-command guidance, language command output, and
|
||||
|
|
|
|||
|
|
@ -417,6 +417,9 @@ keys.
|
|||
|
||||
2. **Stable language identifiers**
|
||||
- Interactive `:lang` accepts only stable language codes: `en` and `zh`.
|
||||
- Code should name this concept `ui_lang`, not `help_lang`; the same value
|
||||
controls prompts, status text, help, command output, MOTD chrome, and
|
||||
system messages.
|
||||
- Locale detection may accept locale-shaped values such as
|
||||
`en_US.UTF-8`, `zh_CN.UTF-8`, `C`, and `POSIX`.
|
||||
- Do not accept natural-language labels such as `english`, `chinese`,
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
#include "common.h"
|
||||
|
||||
void cli_text_append_help(char *buffer, size_t buf_size, size_t *pos,
|
||||
const char *program_name, help_lang_t lang);
|
||||
const char *cli_text_invalid_port_format(help_lang_t lang);
|
||||
const char *cli_text_unknown_option_format(help_lang_t lang);
|
||||
const char *cli_text_short_usage_format(help_lang_t lang);
|
||||
const char *program_name, ui_lang_t lang);
|
||||
const char *cli_text_invalid_port_format(ui_lang_t lang);
|
||||
const char *cli_text_unknown_option_format(ui_lang_t lang);
|
||||
const char *cli_text_short_usage_format(ui_lang_t lang);
|
||||
|
||||
#endif /* CLI_TEXT_H */
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ bool command_catalog_match(const char *line, tnt_command_id_t *id,
|
|||
const char **args);
|
||||
const char *command_catalog_suggest(const char *name);
|
||||
void command_catalog_append_full(char *buffer, size_t buf_size, size_t *pos,
|
||||
help_lang_t lang);
|
||||
ui_lang_t lang);
|
||||
void command_catalog_append_manual(char *buffer, size_t buf_size, size_t *pos,
|
||||
help_lang_t lang);
|
||||
ui_lang_t lang);
|
||||
|
||||
#endif /* COMMAND_CATALOG_H */
|
||||
|
|
|
|||
|
|
@ -44,11 +44,11 @@ typedef enum {
|
|||
MODE_HELP
|
||||
} client_mode_t;
|
||||
|
||||
/* Help language */
|
||||
/* UI language */
|
||||
typedef enum {
|
||||
LANG_EN,
|
||||
LANG_ZH
|
||||
} help_lang_t;
|
||||
UI_LANG_EN,
|
||||
UI_LANG_ZH
|
||||
} ui_lang_t;
|
||||
|
||||
/* Runtime helpers */
|
||||
const char* tnt_state_dir(void);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@
|
|||
#include "common.h"
|
||||
|
||||
void help_text_append_full(char *buffer, size_t buf_size, size_t *pos,
|
||||
help_lang_t lang);
|
||||
ui_lang_t lang);
|
||||
|
||||
#endif /* HELP_TEXT_H */
|
||||
|
|
|
|||
|
|
@ -63,10 +63,10 @@ typedef enum {
|
|||
I18N_EXEC_UNKNOWN_COMMAND_FORMAT
|
||||
} 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_default_lang(void);
|
||||
const char *i18n_lang_code(help_lang_t lang);
|
||||
const char *i18n_text(help_lang_t lang, i18n_text_id_t id);
|
||||
bool i18n_try_parse_ui_lang(const char *value, ui_lang_t *lang);
|
||||
ui_lang_t i18n_parse_ui_lang(const char *value, ui_lang_t fallback);
|
||||
ui_lang_t i18n_default_ui_lang(void);
|
||||
const char *i18n_ui_lang_code(ui_lang_t lang);
|
||||
const char *i18n_text(ui_lang_t lang, i18n_text_id_t id);
|
||||
|
||||
#endif /* I18N_H */
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@
|
|||
#include "common.h"
|
||||
|
||||
void manual_append_interactive_panel(char *buffer, size_t buf_size,
|
||||
size_t *pos, help_lang_t lang);
|
||||
size_t *pos, ui_lang_t lang);
|
||||
|
||||
#endif /* MANUAL_H */
|
||||
|
|
|
|||
|
|
@ -4,6 +4,6 @@
|
|||
#include "common.h"
|
||||
|
||||
void manual_text_append_interactive(char *buffer, size_t buf_size,
|
||||
size_t *pos, help_lang_t lang);
|
||||
size_t *pos, ui_lang_t lang);
|
||||
|
||||
#endif /* MANUAL_TEXT_H */
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ typedef struct client {
|
|||
_Atomic int width;
|
||||
_Atomic int height;
|
||||
client_mode_t mode;
|
||||
help_lang_t help_lang;
|
||||
ui_lang_t ui_lang;
|
||||
int scroll_pos;
|
||||
bool follow_tail; /* NORMAL stays pinned to latest until user scrolls up */
|
||||
int help_scroll_pos;
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
#include "message.h"
|
||||
|
||||
void system_message_make_join(message_t *msg, const char *username,
|
||||
help_lang_t lang);
|
||||
ui_lang_t lang);
|
||||
void system_message_make_leave(message_t *msg, const char *username,
|
||||
help_lang_t lang);
|
||||
ui_lang_t lang);
|
||||
void system_message_make_nick(message_t *msg, const char *old_name,
|
||||
const char *new_name, help_lang_t lang);
|
||||
const char *new_name, ui_lang_t lang);
|
||||
|
||||
bool system_message_is_system(const message_t *msg);
|
||||
bool system_message_is_join_leave(const message_t *msg);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
#include "cli_text.h"
|
||||
|
||||
void cli_text_append_help(char *buffer, size_t buf_size, size_t *pos,
|
||||
const char *program_name, help_lang_t lang) {
|
||||
const char *program_name, ui_lang_t lang) {
|
||||
const char *program = (program_name && program_name[0] != '\0')
|
||||
? program_name
|
||||
: "tnt";
|
||||
|
||||
if (lang == LANG_ZH) {
|
||||
if (lang == UI_LANG_ZH) {
|
||||
buffer_appendf(buffer, buf_size, pos,
|
||||
"tnt %s - 匿名 SSH 聊天服务器\n\n"
|
||||
"用法: %s [选项]\n\n"
|
||||
|
|
@ -48,15 +48,15 @@ void cli_text_append_help(char *buffer, size_t buf_size, size_t *pos,
|
|||
TNT_VERSION, program, DEFAULT_PORT);
|
||||
}
|
||||
|
||||
const char *cli_text_invalid_port_format(help_lang_t lang) {
|
||||
return lang == LANG_ZH ? "端口无效: %s\n" : "Invalid port: %s\n";
|
||||
const char *cli_text_invalid_port_format(ui_lang_t lang) {
|
||||
return lang == UI_LANG_ZH ? "端口无效: %s\n" : "Invalid port: %s\n";
|
||||
}
|
||||
|
||||
const char *cli_text_unknown_option_format(help_lang_t lang) {
|
||||
return lang == LANG_ZH ? "未知选项: %s\n" : "Unknown option: %s\n";
|
||||
const char *cli_text_unknown_option_format(ui_lang_t lang) {
|
||||
return lang == UI_LANG_ZH ? "未知选项: %s\n" : "Unknown option: %s\n";
|
||||
}
|
||||
|
||||
const char *cli_text_short_usage_format(help_lang_t lang) {
|
||||
return lang == LANG_ZH ? "用法: %s [-p PORT] [-d DIR] [-h]\n"
|
||||
const char *cli_text_short_usage_format(ui_lang_t lang) {
|
||||
return lang == UI_LANG_ZH ? "用法: %s [-p PORT] [-d DIR] [-h]\n"
|
||||
: "Usage: %s [-p PORT] [-d DIR] [-h]\n";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -221,11 +221,11 @@ const char *command_catalog_suggest(const char *name) {
|
|||
}
|
||||
|
||||
void command_catalog_append_full(char *buffer, size_t buf_size, size_t *pos,
|
||||
help_lang_t lang) {
|
||||
ui_lang_t lang) {
|
||||
for (size_t i = 0; i < sizeof(entries) / sizeof(entries[0]); i++) {
|
||||
const char *usage = lang == LANG_ZH ? entries[i].full_usage_zh
|
||||
const char *usage = lang == UI_LANG_ZH ? entries[i].full_usage_zh
|
||||
: entries[i].full_usage_en;
|
||||
const char *summary = lang == LANG_ZH ? entries[i].summary_zh
|
||||
const char *summary = lang == UI_LANG_ZH ? entries[i].summary_zh
|
||||
: entries[i].summary_en;
|
||||
buffer_appendf(buffer, buf_size, pos, " %-40s - %s\n",
|
||||
usage, summary);
|
||||
|
|
@ -233,7 +233,7 @@ void command_catalog_append_full(char *buffer, size_t buf_size, size_t *pos,
|
|||
}
|
||||
|
||||
void command_catalog_append_manual(char *buffer, size_t buf_size, size_t *pos,
|
||||
help_lang_t lang) {
|
||||
ui_lang_t lang) {
|
||||
for (int group = 1; group <= 3; group++) {
|
||||
bool first = true;
|
||||
|
||||
|
|
@ -243,7 +243,7 @@ void command_catalog_append_manual(char *buffer, size_t buf_size, size_t *pos,
|
|||
if (entries[i].manual_group != group) {
|
||||
continue;
|
||||
}
|
||||
usage = lang == LANG_ZH ? entries[i].manual_usage_zh
|
||||
usage = lang == UI_LANG_ZH ? entries[i].manual_usage_zh
|
||||
: entries[i].manual_usage_en;
|
||||
if (!usage) {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -93,17 +93,17 @@ void commands_dispatch(client_t *client) {
|
|||
if (!command_catalog_match(cmd, &command_id, &arg)) {
|
||||
const char *suggestion = command_catalog_suggest(cmd);
|
||||
buffer_appendf(output, sizeof(output), &pos,
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_UNKNOWN_COMMAND_FORMAT),
|
||||
cmd);
|
||||
if (suggestion) {
|
||||
buffer_appendf(output, sizeof(output), &pos,
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_DID_YOU_MEAN_FORMAT),
|
||||
suggestion);
|
||||
}
|
||||
buffer_appendf(output, sizeof(output), &pos, "%s",
|
||||
i18n_text(client->help_lang, I18N_UNKNOWN_GUIDANCE));
|
||||
i18n_text(client->ui_lang, I18N_UNKNOWN_GUIDANCE));
|
||||
goto cmd_done;
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +112,7 @@ void commands_dispatch(client_t *client) {
|
|||
int total = g_room->client_count;
|
||||
buffer_appendf(output, sizeof(output), &pos,
|
||||
"\033[1;36m%s\033[0m \033[2;37m· %d\033[0m\n",
|
||||
i18n_text(client->help_lang, I18N_USERS_TITLE), total);
|
||||
i18n_text(client->ui_lang, I18N_USERS_TITLE), total);
|
||||
|
||||
time_t now = time(NULL);
|
||||
for (int i = 0; i < total; i++) {
|
||||
|
|
@ -137,25 +137,25 @@ void commands_dispatch(client_t *client) {
|
|||
|
||||
} else if (command_id == TNT_COMMAND_HELP) {
|
||||
manual_append_interactive_panel(output, sizeof(output), &pos,
|
||||
client->help_lang);
|
||||
client->ui_lang);
|
||||
|
||||
} else if (command_id == TNT_COMMAND_LANG) {
|
||||
help_lang_t next_lang;
|
||||
ui_lang_t next_lang;
|
||||
|
||||
if (!arg || arg[0] == '\0') {
|
||||
buffer_appendf(output, sizeof(output), &pos,
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_LANG_CURRENT_FORMAT),
|
||||
i18n_lang_code(client->help_lang));
|
||||
} else if (i18n_try_parse_lang(arg, &next_lang)) {
|
||||
client->help_lang = next_lang;
|
||||
i18n_ui_lang_code(client->ui_lang));
|
||||
} else if (i18n_try_parse_ui_lang(arg, &next_lang)) {
|
||||
client->ui_lang = next_lang;
|
||||
buffer_appendf(output, sizeof(output), &pos,
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_LANG_SET_FORMAT),
|
||||
i18n_lang_code(client->help_lang));
|
||||
i18n_ui_lang_code(client->ui_lang));
|
||||
} else {
|
||||
buffer_appendf(output, sizeof(output), &pos,
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_LANG_UNSUPPORTED_FORMAT),
|
||||
arg);
|
||||
}
|
||||
|
|
@ -172,7 +172,7 @@ void commands_dispatch(client_t *client) {
|
|||
|
||||
if (target_name[0] == '\0' || rest[0] == '\0') {
|
||||
buffer_appendf(output, sizeof(output), &pos, "%s",
|
||||
i18n_text(client->help_lang, I18N_MSG_USAGE));
|
||||
i18n_text(client->ui_lang, I18N_MSG_USAGE));
|
||||
} else {
|
||||
bool found = false;
|
||||
client_t *target = NULL;
|
||||
|
|
@ -220,12 +220,12 @@ void commands_dispatch(client_t *client) {
|
|||
|
||||
if (found) {
|
||||
buffer_appendf(output, sizeof(output), &pos,
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_MSG_SENT_FORMAT),
|
||||
target_name);
|
||||
} else {
|
||||
buffer_appendf(output, sizeof(output), &pos,
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_MSG_USER_NOT_FOUND_FORMAT),
|
||||
target_name);
|
||||
}
|
||||
|
|
@ -245,12 +245,12 @@ void commands_dispatch(client_t *client) {
|
|||
|
||||
buffer_appendf(output, sizeof(output), &pos,
|
||||
"\033[1;36m%s\033[0m \033[2;37m· %d\033[0m\n",
|
||||
i18n_text(client->help_lang, I18N_INBOX_TITLE),
|
||||
i18n_text(client->ui_lang, I18N_INBOX_TITLE),
|
||||
snap_count);
|
||||
if (snap_count == 0) {
|
||||
buffer_appendf(output, sizeof(output), &pos,
|
||||
" \033[2;37m%s\033[0m\n",
|
||||
i18n_text(client->help_lang, I18N_INBOX_EMPTY));
|
||||
i18n_text(client->ui_lang, I18N_INBOX_EMPTY));
|
||||
}
|
||||
for (int i = 0; i < snap_count; i++) {
|
||||
char ts[20];
|
||||
|
|
@ -268,10 +268,10 @@ void commands_dispatch(client_t *client) {
|
|||
|
||||
if (new_name[0] == '\0') {
|
||||
buffer_appendf(output, sizeof(output), &pos, "%s",
|
||||
i18n_text(client->help_lang, I18N_NICK_USAGE));
|
||||
i18n_text(client->ui_lang, I18N_NICK_USAGE));
|
||||
} else if (!is_valid_username(new_name)) {
|
||||
buffer_appendf(output, sizeof(output), &pos, "%s",
|
||||
i18n_text(client->help_lang, I18N_NICK_INVALID));
|
||||
i18n_text(client->ui_lang, I18N_NICK_INVALID));
|
||||
} else {
|
||||
char validated_name[MAX_USERNAME_LEN];
|
||||
snprintf(validated_name, sizeof(validated_name), "%s", new_name);
|
||||
|
|
@ -303,22 +303,22 @@ void commands_dispatch(client_t *client) {
|
|||
|
||||
if (taken) {
|
||||
buffer_appendf(output, sizeof(output), &pos,
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_NICK_TAKEN_FORMAT),
|
||||
validated_name);
|
||||
} else if (strcmp(validated_name, old_name) == 0) {
|
||||
buffer_appendf(output, sizeof(output), &pos, "%s",
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_NICK_UNCHANGED));
|
||||
} else {
|
||||
message_t nick_msg;
|
||||
system_message_make_nick(&nick_msg, old_name,
|
||||
client->username, client->help_lang);
|
||||
client->username, client->ui_lang);
|
||||
room_broadcast(g_room, &nick_msg);
|
||||
message_save(&nick_msg);
|
||||
|
||||
buffer_appendf(output, sizeof(output), &pos,
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_NICK_CHANGED_FORMAT),
|
||||
old_name, client->username);
|
||||
}
|
||||
|
|
@ -332,7 +332,7 @@ void commands_dispatch(client_t *client) {
|
|||
long val = strtol(arg, &endp, 10);
|
||||
if (*endp != '\0' || val < 1 || val > 50) {
|
||||
buffer_appendf(output, sizeof(output), &pos, "%s",
|
||||
i18n_text(client->help_lang, I18N_LAST_USAGE));
|
||||
i18n_text(client->ui_lang, I18N_LAST_USAGE));
|
||||
goto cmd_done;
|
||||
}
|
||||
n = (int)val;
|
||||
|
|
@ -341,7 +341,7 @@ void commands_dispatch(client_t *client) {
|
|||
message_t *last_msgs = NULL;
|
||||
int last_count = message_load(&last_msgs, n);
|
||||
buffer_appendf(output, sizeof(output), &pos,
|
||||
i18n_text(client->help_lang, I18N_LAST_HEADER_FORMAT),
|
||||
i18n_text(client->ui_lang, I18N_LAST_HEADER_FORMAT),
|
||||
last_count);
|
||||
for (int i = 0; i < last_count; i++) {
|
||||
char ts[20];
|
||||
|
|
@ -358,12 +358,12 @@ void commands_dispatch(client_t *client) {
|
|||
while (*query == ' ') query++;
|
||||
if (*query == '\0') {
|
||||
buffer_appendf(output, sizeof(output), &pos, "%s",
|
||||
i18n_text(client->help_lang, I18N_SEARCH_USAGE));
|
||||
i18n_text(client->ui_lang, I18N_SEARCH_USAGE));
|
||||
} else {
|
||||
message_t *found = NULL;
|
||||
int found_count = message_search(query, &found, 15);
|
||||
buffer_appendf(output, sizeof(output), &pos,
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_SEARCH_HEADER_FORMAT),
|
||||
query, found_count);
|
||||
for (int i = 0; i < found_count; i++) {
|
||||
|
|
@ -386,8 +386,8 @@ void commands_dispatch(client_t *client) {
|
|||
} else if (command_id == TNT_COMMAND_MUTE_JOINS) {
|
||||
client->mute_joins = !client->mute_joins;
|
||||
buffer_appendf(output, sizeof(output), &pos,
|
||||
i18n_text(client->help_lang, I18N_MUTE_JOINS_FORMAT),
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang, I18N_MUTE_JOINS_FORMAT),
|
||||
i18n_text(client->ui_lang,
|
||||
client->mute_joins ?
|
||||
I18N_MUTE_JOINS_MUTED :
|
||||
I18N_MUTE_JOINS_UNMUTED));
|
||||
|
|
@ -398,7 +398,7 @@ void commands_dispatch(client_t *client) {
|
|||
|
||||
} else if (command_id == TNT_COMMAND_CLEAR) {
|
||||
buffer_appendf(output, sizeof(output), &pos, "%s",
|
||||
i18n_text(client->help_lang, I18N_CLEAR_DONE));
|
||||
i18n_text(client->ui_lang, I18N_CLEAR_DONE));
|
||||
}
|
||||
|
||||
cmd_done:
|
||||
|
|
|
|||
16
src/exec.c
16
src/exec.c
|
|
@ -116,7 +116,7 @@ static void resolve_exec_username(const client_t *client, char *buffer,
|
|||
}
|
||||
|
||||
static int exec_command_help(client_t *client) {
|
||||
const char *help_text = i18n_text(client->help_lang, I18N_EXEC_HELP);
|
||||
const char *help_text = i18n_text(client->ui_lang, I18N_EXEC_HELP);
|
||||
|
||||
return client_send(client, help_text, strlen(help_text)) == 0 ? 0 : 1;
|
||||
}
|
||||
|
|
@ -285,7 +285,7 @@ static int exec_command_tail(client_t *client, const char *args) {
|
|||
|
||||
if (parse_tail_count(args, &requested) < 0) {
|
||||
client_printf(client, "%s",
|
||||
i18n_text(client->help_lang, I18N_EXEC_TAIL_USAGE));
|
||||
i18n_text(client->ui_lang, I18N_EXEC_TAIL_USAGE));
|
||||
return 64;
|
||||
}
|
||||
|
||||
|
|
@ -339,7 +339,7 @@ static int exec_command_post(client_t *client, const char *args) {
|
|||
|
||||
if (!args || args[0] == '\0') {
|
||||
client_printf(client, "%s",
|
||||
i18n_text(client->help_lang, I18N_EXEC_POST_USAGE));
|
||||
i18n_text(client->ui_lang, I18N_EXEC_POST_USAGE));
|
||||
return 64;
|
||||
}
|
||||
|
||||
|
|
@ -349,13 +349,13 @@ static int exec_command_post(client_t *client, const char *args) {
|
|||
|
||||
if (content[0] == '\0') {
|
||||
client_printf(client, "%s",
|
||||
i18n_text(client->help_lang, I18N_EXEC_POST_EMPTY));
|
||||
i18n_text(client->ui_lang, I18N_EXEC_POST_EMPTY));
|
||||
return 64;
|
||||
}
|
||||
|
||||
if (!utf8_is_valid_string(content)) {
|
||||
client_printf(client, "%s",
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_EXEC_POST_INVALID_UTF8));
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -426,7 +426,7 @@ int exec_dispatch(client_t *client) {
|
|||
if (strcmp(cmd, "users") == 0) {
|
||||
if (args && strcmp(args, "--json") != 0) {
|
||||
client_printf(client, "%s",
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_EXEC_USERS_USAGE));
|
||||
return 64;
|
||||
}
|
||||
|
|
@ -435,7 +435,7 @@ int exec_dispatch(client_t *client) {
|
|||
if (strcmp(cmd, "stats") == 0) {
|
||||
if (args && strcmp(args, "--json") != 0) {
|
||||
client_printf(client, "%s",
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_EXEC_STATS_USAGE));
|
||||
return 64;
|
||||
}
|
||||
|
|
@ -452,7 +452,7 @@ int exec_dispatch(client_t *client) {
|
|||
}
|
||||
|
||||
client_printf(client,
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_EXEC_UNKNOWN_COMMAND_FORMAT),
|
||||
cmd);
|
||||
return 64;
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
#include "command_catalog.h"
|
||||
|
||||
void help_text_append_full(char *buffer, size_t buf_size, size_t *pos,
|
||||
help_lang_t lang) {
|
||||
if (lang == LANG_EN) {
|
||||
ui_lang_t lang) {
|
||||
if (lang == UI_LANG_EN) {
|
||||
buffer_appendf(buffer, buf_size, pos,
|
||||
"TNT KEY REFERENCE\n"
|
||||
"\n"
|
||||
|
|
|
|||
26
src/i18n.c
26
src/i18n.c
|
|
@ -36,38 +36,38 @@ static bool starts_with_lang(const char *value, const char *prefix) {
|
|||
return is_lang_boundary(value);
|
||||
}
|
||||
|
||||
bool i18n_try_parse_lang(const char *value, help_lang_t *lang) {
|
||||
bool i18n_try_parse_ui_lang(const char *value, ui_lang_t *lang) {
|
||||
if (!value || value[0] == '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (starts_with_lang(value, "zh")) {
|
||||
if (lang) *lang = LANG_ZH;
|
||||
if (lang) *lang = UI_LANG_ZH;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (starts_with_lang(value, "en") ||
|
||||
starts_with_lang(value, "c") ||
|
||||
starts_with_lang(value, "posix")) {
|
||||
if (lang) *lang = LANG_EN;
|
||||
if (lang) *lang = UI_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)) {
|
||||
ui_lang_t i18n_parse_ui_lang(const char *value, ui_lang_t fallback) {
|
||||
ui_lang_t lang;
|
||||
if (i18n_try_parse_ui_lang(value, &lang)) {
|
||||
return lang;
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
help_lang_t i18n_default_lang(void) {
|
||||
ui_lang_t i18n_default_ui_lang(void) {
|
||||
const char *explicit_lang = getenv("TNT_LANG");
|
||||
if (explicit_lang && explicit_lang[0] != '\0') {
|
||||
return i18n_parse_lang(explicit_lang, LANG_EN);
|
||||
return i18n_parse_ui_lang(explicit_lang, UI_LANG_EN);
|
||||
}
|
||||
|
||||
const char *locale = getenv("LC_ALL");
|
||||
|
|
@ -78,15 +78,15 @@ help_lang_t i18n_default_lang(void) {
|
|||
locale = getenv("LANG");
|
||||
}
|
||||
|
||||
return i18n_parse_lang(locale, LANG_EN);
|
||||
return i18n_parse_ui_lang(locale, UI_LANG_EN);
|
||||
}
|
||||
|
||||
const char *i18n_lang_code(help_lang_t lang) {
|
||||
return lang == LANG_ZH ? "zh" : "en";
|
||||
const char *i18n_ui_lang_code(ui_lang_t lang) {
|
||||
return lang == UI_LANG_ZH ? "zh" : "en";
|
||||
}
|
||||
|
||||
const char *i18n_text(help_lang_t lang, i18n_text_id_t id) {
|
||||
if (lang == LANG_ZH) {
|
||||
const char *i18n_text(ui_lang_t lang, i18n_text_id_t id) {
|
||||
if (lang == UI_LANG_ZH) {
|
||||
switch (id) {
|
||||
case I18N_USERNAME_PROMPT:
|
||||
return " 请输入用户名 (留空 anonymous): ";
|
||||
|
|
|
|||
22
src/input.c
22
src/input.c
|
|
@ -21,11 +21,11 @@
|
|||
#include <time.h>
|
||||
|
||||
static int g_idle_timeout = DEFAULT_IDLE_TIMEOUT;
|
||||
static help_lang_t g_default_lang = LANG_EN;
|
||||
static ui_lang_t g_default_ui_lang = UI_LANG_EN;
|
||||
|
||||
void input_init(void) {
|
||||
g_idle_timeout = env_int("TNT_IDLE_TIMEOUT", DEFAULT_IDLE_TIMEOUT, 0, 86400);
|
||||
g_default_lang = i18n_default_lang();
|
||||
g_default_ui_lang = i18n_default_ui_lang();
|
||||
}
|
||||
|
||||
static int read_username(client_t *client) {
|
||||
|
|
@ -34,7 +34,7 @@ static int read_username(client_t *client) {
|
|||
char buf[4];
|
||||
|
||||
tui_render_welcome(client);
|
||||
client_printf(client, "%s", i18n_text(client->help_lang,
|
||||
client_printf(client, "%s", i18n_text(client->ui_lang,
|
||||
I18N_USERNAME_PROMPT));
|
||||
|
||||
while (1) {
|
||||
|
|
@ -117,7 +117,7 @@ static int read_username(client_t *client) {
|
|||
|
||||
/* Validate username for security */
|
||||
if (!is_valid_username(client->username)) {
|
||||
client_printf(client, "%s", i18n_text(client->help_lang,
|
||||
client_printf(client, "%s", i18n_text(client->ui_lang,
|
||||
I18N_INVALID_USERNAME));
|
||||
strcpy(client->username, "anonymous");
|
||||
} else {
|
||||
|
|
@ -258,11 +258,11 @@ static bool handle_key(client_t *client, unsigned char key, char *input) {
|
|||
client->show_help = false;
|
||||
tui_render_screen(client);
|
||||
} else if (key == 'e' || key == 'E') {
|
||||
client->help_lang = LANG_EN;
|
||||
client->ui_lang = UI_LANG_EN;
|
||||
client->help_scroll_pos = 0;
|
||||
tui_render_help(client);
|
||||
} else if (key == 'z' || key == 'Z') {
|
||||
client->help_lang = LANG_ZH;
|
||||
client->ui_lang = UI_LANG_ZH;
|
||||
client->help_scroll_pos = 0;
|
||||
tui_render_help(client);
|
||||
} else if (key == 'j') {
|
||||
|
|
@ -726,7 +726,7 @@ void input_run_session(client_t *client) {
|
|||
/* Terminal size already set from PTY request */
|
||||
client->mode = MODE_INSERT;
|
||||
client->follow_tail = true;
|
||||
client->help_lang = g_default_lang;
|
||||
client->ui_lang = g_default_ui_lang;
|
||||
client->connected = true;
|
||||
client->command_history_count = 0;
|
||||
client->command_history_pos = 0;
|
||||
|
|
@ -751,7 +751,7 @@ void input_run_session(client_t *client) {
|
|||
|
||||
/* Add to room */
|
||||
if (room_add_client(g_room, client) < 0) {
|
||||
client_printf(client, "%s", i18n_text(client->help_lang,
|
||||
client_printf(client, "%s", i18n_text(client->ui_lang,
|
||||
I18N_ROOM_FULL));
|
||||
goto cleanup;
|
||||
}
|
||||
|
|
@ -765,7 +765,7 @@ void input_run_session(client_t *client) {
|
|||
|
||||
/* Broadcast join message */
|
||||
message_t join_msg;
|
||||
system_message_make_join(&join_msg, client->username, client->help_lang);
|
||||
system_message_make_join(&join_msg, client->username, client->ui_lang);
|
||||
room_broadcast(g_room, &join_msg);
|
||||
message_save(&join_msg);
|
||||
|
||||
|
|
@ -851,7 +851,7 @@ main_loop:
|
|||
if (g_idle_timeout > 0 && joined_room &&
|
||||
time(NULL) - client->last_active >= g_idle_timeout) {
|
||||
client_printf(client,
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_IDLE_TIMEOUT_FORMAT),
|
||||
g_idle_timeout / 60);
|
||||
break;
|
||||
|
|
@ -956,7 +956,7 @@ cleanup:
|
|||
if (joined_room) {
|
||||
message_t leave_msg;
|
||||
system_message_make_leave(&leave_msg, client->username,
|
||||
client->help_lang);
|
||||
client->ui_lang);
|
||||
|
||||
client->connected = false;
|
||||
room_remove_client(g_room, client);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ static void signal_handler(int sig) {
|
|||
|
||||
int main(int argc, char **argv) {
|
||||
int port = DEFAULT_PORT;
|
||||
help_lang_t lang = i18n_default_lang();
|
||||
ui_lang_t lang = i18n_default_ui_lang();
|
||||
|
||||
/* Environment provides defaults; command-line flags override it. */
|
||||
const char *port_env = getenv("PORT");
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#include "manual_text.h"
|
||||
|
||||
void manual_append_interactive_panel(char *buffer, size_t buf_size,
|
||||
size_t *pos, help_lang_t lang) {
|
||||
size_t *pos, ui_lang_t lang) {
|
||||
if (!buffer || !pos) return;
|
||||
|
||||
manual_text_append_interactive(buffer, buf_size, pos, lang);
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
#include "command_catalog.h"
|
||||
|
||||
void manual_text_append_interactive(char *buffer, size_t buf_size,
|
||||
size_t *pos, help_lang_t lang) {
|
||||
if (lang == LANG_ZH) {
|
||||
size_t *pos, ui_lang_t lang) {
|
||||
if (lang == UI_LANG_ZH) {
|
||||
buffer_appendf(buffer, buf_size, pos,
|
||||
"\033[1;36mTNT(1) 帮助\033[0m\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
static void system_message_init(message_t *msg, help_lang_t lang) {
|
||||
static void system_message_init(message_t *msg, ui_lang_t lang) {
|
||||
if (!msg) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@ static void system_message_init(message_t *msg, help_lang_t lang) {
|
|||
}
|
||||
|
||||
void system_message_make_join(message_t *msg, const char *username,
|
||||
help_lang_t lang) {
|
||||
ui_lang_t lang) {
|
||||
system_message_init(msg, lang);
|
||||
if (!msg) {
|
||||
return;
|
||||
|
|
@ -28,7 +28,7 @@ void system_message_make_join(message_t *msg, const char *username,
|
|||
}
|
||||
|
||||
void system_message_make_leave(message_t *msg, const char *username,
|
||||
help_lang_t lang) {
|
||||
ui_lang_t lang) {
|
||||
system_message_init(msg, lang);
|
||||
if (!msg) {
|
||||
return;
|
||||
|
|
@ -40,7 +40,7 @@ void system_message_make_leave(message_t *msg, const char *username,
|
|||
}
|
||||
|
||||
void system_message_make_nick(message_t *msg, const char *old_name,
|
||||
const char *new_name, help_lang_t lang) {
|
||||
const char *new_name, ui_lang_t lang) {
|
||||
system_message_init(msg, lang);
|
||||
if (!msg) {
|
||||
return;
|
||||
|
|
|
|||
26
src/tui.c
26
src/tui.c
|
|
@ -154,8 +154,8 @@ void tui_render_welcome(client_t *client) {
|
|||
|
||||
/* Lines, in display order. Width is computed in display columns. */
|
||||
const char *line1 = "TNT · " TNT_VERSION;
|
||||
const char *line2 = i18n_text(client->help_lang, I18N_WELCOME_SUBTITLE);
|
||||
const char *line3 = i18n_text(client->help_lang, I18N_WELCOME_TAGLINE);
|
||||
const char *line2 = i18n_text(client->ui_lang, I18N_WELCOME_SUBTITLE);
|
||||
const char *line3 = i18n_text(client->ui_lang, I18N_WELCOME_TAGLINE);
|
||||
|
||||
int inner_w = utf8_string_width(line1);
|
||||
int w2 = utf8_string_width(line2);
|
||||
|
|
@ -169,7 +169,7 @@ void tui_render_welcome(client_t *client) {
|
|||
char fallback_text[96];
|
||||
char fallback[128];
|
||||
snprintf(fallback_text, sizeof(fallback_text),
|
||||
i18n_text(client->help_lang, I18N_WELCOME_FALLBACK_FORMAT),
|
||||
i18n_text(client->ui_lang, I18N_WELCOME_FALLBACK_FORMAT),
|
||||
TNT_VERSION);
|
||||
int n = snprintf(fallback, sizeof(fallback), ANSI_CLEAR ANSI_HOME "%s",
|
||||
fallback_text);
|
||||
|
|
@ -355,7 +355,7 @@ void tui_render_screen(client_t *client) {
|
|||
|
||||
char online_buf[32];
|
||||
snprintf(online_buf, sizeof(online_buf),
|
||||
i18n_text(client->help_lang, I18N_TITLE_ONLINE_FORMAT),
|
||||
i18n_text(client->ui_lang, I18N_TITLE_ONLINE_FORMAT),
|
||||
online);
|
||||
chips[chip_count].value = online_buf;
|
||||
chips[chip_count].value_color = "\033[37m";
|
||||
|
|
@ -373,9 +373,9 @@ void tui_render_screen(client_t *client) {
|
|||
chips[chip_count].value_color = mode_color;
|
||||
chip_count++;
|
||||
|
||||
const char *hint = i18n_text(client->help_lang, I18N_TITLE_HELP_HINT);
|
||||
const char *hint = i18n_text(client->ui_lang, I18N_TITLE_HELP_HINT);
|
||||
int hint_width = utf8_string_width(hint);
|
||||
const char *mute_label = i18n_text(client->help_lang, I18N_TITLE_MUTED);
|
||||
const char *mute_label = i18n_text(client->ui_lang, I18N_TITLE_MUTED);
|
||||
int mute_width = client->mute_joins ? utf8_string_width(mute_label) + 2 : 0;
|
||||
|
||||
/* Unread @-mentions chip — high-priority, gets a bright yellow star.
|
||||
|
|
@ -623,7 +623,7 @@ void tui_render_command_output(client_t *client) {
|
|||
buffer_appendf(buffer, sizeof(buffer), &pos, ANSI_CLEAR ANSI_HOME);
|
||||
|
||||
/* Title */
|
||||
const char *title = i18n_text(client->help_lang,
|
||||
const char *title = i18n_text(client->ui_lang,
|
||||
I18N_COMMAND_OUTPUT_TITLE);
|
||||
char title_display[64];
|
||||
utf8_ansi_truncate(title, title_display, sizeof(title_display), rw);
|
||||
|
|
@ -676,7 +676,7 @@ void tui_render_command_output(client_t *client) {
|
|||
}
|
||||
|
||||
buffer_appendf(buffer, sizeof(buffer), &pos,
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_COMMAND_OUTPUT_STATUS_FORMAT),
|
||||
start + 1, max_scroll + 1);
|
||||
|
||||
|
|
@ -706,7 +706,7 @@ void tui_render_motd(client_t *client) {
|
|||
buffer_appendf(buffer, sizeof(buffer), &pos, ANSI_CLEAR ANSI_HOME);
|
||||
|
||||
/* Top border with a localized title chip. */
|
||||
const char *title = i18n_text(client->help_lang, I18N_MOTD_TITLE);
|
||||
const char *title = i18n_text(client->ui_lang, I18N_MOTD_TITLE);
|
||||
int title_w = utf8_string_width(title);
|
||||
int top_dash_fill = rw - 2 - title_w - 1; /* 2 corners, 1 leading ─ */
|
||||
if (top_dash_fill < 0) top_dash_fill = 0;
|
||||
|
|
@ -758,7 +758,7 @@ void tui_render_motd(client_t *client) {
|
|||
buffer_appendf(buffer, sizeof(buffer), &pos, "\r\n");
|
||||
|
||||
/* Bottom border with a localized continue hint. */
|
||||
const char *footer = i18n_text(client->help_lang,
|
||||
const char *footer = i18n_text(client->ui_lang,
|
||||
I18N_MOTD_CONTINUE_HINT);
|
||||
int footer_w = utf8_string_width(footer);
|
||||
int bot_dash_fill = rw - 2 - footer_w - 1;
|
||||
|
|
@ -790,7 +790,7 @@ void tui_render_help(client_t *client) {
|
|||
buffer_appendf(buffer, sizeof(buffer), &pos, ANSI_CLEAR ANSI_HOME);
|
||||
|
||||
/* Title */
|
||||
const char *title = i18n_text(client->help_lang, I18N_HELP_TITLE);
|
||||
const char *title = i18n_text(client->ui_lang, I18N_HELP_TITLE);
|
||||
int title_width = utf8_string_width(title);
|
||||
int padding = rw - title_width;
|
||||
if (padding < 0) padding = 0;
|
||||
|
|
@ -805,7 +805,7 @@ void tui_render_help(client_t *client) {
|
|||
size_t help_pos = 0;
|
||||
help_copy[0] = '\0';
|
||||
help_text_append_full(help_copy, sizeof(help_copy), &help_pos,
|
||||
client->help_lang);
|
||||
client->ui_lang);
|
||||
|
||||
/* Split into lines and display with scrolling */
|
||||
char *lines[100];
|
||||
|
|
@ -836,7 +836,7 @@ void tui_render_help(client_t *client) {
|
|||
|
||||
/* Status line */
|
||||
buffer_appendf(buffer, sizeof(buffer), &pos,
|
||||
i18n_text(client->help_lang, I18N_HELP_STATUS_FORMAT),
|
||||
i18n_text(client->ui_lang, I18N_HELP_STATUS_FORMAT),
|
||||
start + 1, max_scroll + 1);
|
||||
|
||||
client_send(client, buffer, pos);
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ void tui_status_append(char *buffer, size_t buf_size, size_t *pos,
|
|||
"\033[2;37m›\033[0m "
|
||||
"\033[2;37m%s\033[0m"
|
||||
"\033[K",
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_INSERT_HINT_WIDE));
|
||||
} else if (client->width >= 36) {
|
||||
buffer_appendf(buffer, buf_size, pos,
|
||||
"\033[2;37m›\033[0m "
|
||||
"\033[2;37m%s\033[0m\033[K",
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_INSERT_HINT_NARROW));
|
||||
} else {
|
||||
buffer_appendf(buffer, buf_size, pos, "\033[2;37m›\033[0m \033[K");
|
||||
|
|
@ -36,16 +36,16 @@ void tui_status_append(char *buffer, size_t buf_size, size_t *pos,
|
|||
" \033[2;37m%d-%d / %d\033[0m"
|
||||
" \033[33m▼ %d %s · %s\033[0m\033[K",
|
||||
range_start, range_end, total, unseen,
|
||||
i18n_text(client->help_lang,
|
||||
i18n_text(client->ui_lang,
|
||||
I18N_NORMAL_NEW_MESSAGES),
|
||||
i18n_text(client->help_lang, I18N_NORMAL_LATEST));
|
||||
i18n_text(client->ui_lang, I18N_NORMAL_LATEST));
|
||||
} else {
|
||||
buffer_appendf(buffer, buf_size, pos,
|
||||
"\033[7;33m NORMAL \033[0m"
|
||||
" \033[2;37m%d-%d / %d\033[0m"
|
||||
" \033[2;37m%s\033[0m\033[K",
|
||||
range_start, range_end, total,
|
||||
i18n_text(client->help_lang, I18N_NORMAL_LATEST));
|
||||
i18n_text(client->ui_lang, I18N_NORMAL_LATEST));
|
||||
}
|
||||
} else if (client->mode == MODE_COMMAND) {
|
||||
buffer_appendf(buffer, buf_size, pos,
|
||||
|
|
|
|||
|
|
@ -19,31 +19,31 @@ TEST(help_matches_language) {
|
|||
char output[2048] = {0};
|
||||
size_t pos = 0;
|
||||
|
||||
cli_text_append_help(output, sizeof(output), &pos, "tnt", LANG_EN);
|
||||
cli_text_append_help(output, sizeof(output), &pos, "tnt", UI_LANG_EN);
|
||||
assert(strstr(output, "anonymous SSH chat server") != NULL);
|
||||
assert(strstr(output, "Usage: tnt [options]") != NULL);
|
||||
assert(strstr(output, "TNT_LANG") != NULL);
|
||||
|
||||
memset(output, 0, sizeof(output));
|
||||
pos = 0;
|
||||
cli_text_append_help(output, sizeof(output), &pos, "tnt", LANG_ZH);
|
||||
cli_text_append_help(output, sizeof(output), &pos, "tnt", UI_LANG_ZH);
|
||||
assert(strstr(output, "匿名 SSH 聊天服务器") != NULL);
|
||||
assert(strstr(output, "用法: tnt [选项]") != NULL);
|
||||
assert(strstr(output, "TNT_LANG") != NULL);
|
||||
}
|
||||
|
||||
TEST(error_formats_match_language) {
|
||||
assert(strcmp(cli_text_invalid_port_format(LANG_EN),
|
||||
assert(strcmp(cli_text_invalid_port_format(UI_LANG_EN),
|
||||
"Invalid port: %s\n") == 0);
|
||||
assert(strcmp(cli_text_invalid_port_format(LANG_ZH),
|
||||
assert(strcmp(cli_text_invalid_port_format(UI_LANG_ZH),
|
||||
"端口无效: %s\n") == 0);
|
||||
assert(strcmp(cli_text_unknown_option_format(LANG_EN),
|
||||
assert(strcmp(cli_text_unknown_option_format(UI_LANG_EN),
|
||||
"Unknown option: %s\n") == 0);
|
||||
assert(strcmp(cli_text_unknown_option_format(LANG_ZH),
|
||||
assert(strcmp(cli_text_unknown_option_format(UI_LANG_ZH),
|
||||
"未知选项: %s\n") == 0);
|
||||
assert(strcmp(cli_text_short_usage_format(LANG_EN),
|
||||
assert(strcmp(cli_text_short_usage_format(UI_LANG_EN),
|
||||
"Usage: %s [-p PORT] [-d DIR] [-h]\n") == 0);
|
||||
assert(strcmp(cli_text_short_usage_format(LANG_ZH),
|
||||
assert(strcmp(cli_text_short_usage_format(UI_LANG_ZH),
|
||||
"用法: %s [-p PORT] [-d DIR] [-h]\n") == 0);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,8 +61,8 @@ TEST(generates_localized_help_sections) {
|
|||
size_t en_pos = 0;
|
||||
size_t zh_pos = 0;
|
||||
|
||||
command_catalog_append_full(en, sizeof(en), &en_pos, LANG_EN);
|
||||
command_catalog_append_full(zh, sizeof(zh), &zh_pos, LANG_ZH);
|
||||
command_catalog_append_full(en, sizeof(en), &en_pos, UI_LANG_EN);
|
||||
command_catalog_append_full(zh, sizeof(zh), &zh_pos, UI_LANG_ZH);
|
||||
|
||||
assert(strstr(en, ":users, :list, :who") != NULL);
|
||||
assert(strstr(en, "Show online users") != NULL);
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ TEST(full_help_matches_language) {
|
|||
size_t en_pos = 0;
|
||||
size_t zh_pos = 0;
|
||||
|
||||
help_text_append_full(en, sizeof(en), &en_pos, LANG_EN);
|
||||
help_text_append_full(zh, sizeof(zh), &zh_pos, LANG_ZH);
|
||||
help_text_append_full(en, sizeof(en), &en_pos, UI_LANG_EN);
|
||||
help_text_append_full(zh, sizeof(zh), &zh_pos, UI_LANG_ZH);
|
||||
|
||||
assert(strstr(en, "TNT KEY REFERENCE") != NULL);
|
||||
assert(strstr(en, "AVAILABLE COMMANDS") != NULL);
|
||||
|
|
|
|||
|
|
@ -17,133 +17,133 @@
|
|||
static int tests_passed = 0;
|
||||
|
||||
TEST(parse_explicit_languages) {
|
||||
help_lang_t lang;
|
||||
ui_lang_t lang;
|
||||
|
||||
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("en", LANG_ZH) == LANG_EN);
|
||||
assert(i18n_parse_lang("en_US.UTF-8", LANG_ZH) == LANG_EN);
|
||||
assert(i18n_parse_lang("C", LANG_ZH) == LANG_EN);
|
||||
assert(i18n_parse_lang("POSIX", LANG_ZH) == LANG_EN);
|
||||
assert(i18n_parse_ui_lang("zh", UI_LANG_EN) == UI_LANG_ZH);
|
||||
assert(i18n_parse_ui_lang("zh_CN.UTF-8", UI_LANG_EN) == UI_LANG_ZH);
|
||||
assert(i18n_parse_ui_lang("en", UI_LANG_ZH) == UI_LANG_EN);
|
||||
assert(i18n_parse_ui_lang("en_US.UTF-8", UI_LANG_ZH) == UI_LANG_EN);
|
||||
assert(i18n_parse_ui_lang("C", UI_LANG_ZH) == UI_LANG_EN);
|
||||
assert(i18n_parse_ui_lang("POSIX", UI_LANG_ZH) == UI_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("cn", &lang) == false);
|
||||
assert(i18n_try_parse_lang("english", &lang) == false);
|
||||
assert(i18n_try_parse_lang("chinese", &lang) == false);
|
||||
assert(i18n_try_parse_lang("中文", &lang) == false);
|
||||
assert(i18n_try_parse_lang("英文", &lang) == false);
|
||||
assert(i18n_try_parse_lang("fr", &lang) == false);
|
||||
assert(i18n_try_parse_ui_lang("zh", &lang) == true);
|
||||
assert(lang == UI_LANG_ZH);
|
||||
assert(i18n_try_parse_ui_lang("en", &lang) == true);
|
||||
assert(lang == UI_LANG_EN);
|
||||
assert(i18n_try_parse_ui_lang("cn", &lang) == false);
|
||||
assert(i18n_try_parse_ui_lang("english", &lang) == false);
|
||||
assert(i18n_try_parse_ui_lang("chinese", &lang) == false);
|
||||
assert(i18n_try_parse_ui_lang("中文", &lang) == false);
|
||||
assert(i18n_try_parse_ui_lang("英文", &lang) == false);
|
||||
assert(i18n_try_parse_ui_lang("fr", &lang) == false);
|
||||
}
|
||||
|
||||
TEST(parse_unknown_uses_fallback) {
|
||||
assert(i18n_parse_lang(NULL, LANG_ZH) == LANG_ZH);
|
||||
assert(i18n_parse_lang("", LANG_EN) == LANG_EN);
|
||||
assert(i18n_parse_lang("fr_FR.UTF-8", LANG_ZH) == LANG_ZH);
|
||||
assert(i18n_parse_ui_lang(NULL, UI_LANG_ZH) == UI_LANG_ZH);
|
||||
assert(i18n_parse_ui_lang("", UI_LANG_EN) == UI_LANG_EN);
|
||||
assert(i18n_parse_ui_lang("fr_FR.UTF-8", UI_LANG_ZH) == UI_LANG_ZH);
|
||||
}
|
||||
|
||||
TEST(parse_ignores_surrounding_whitespace) {
|
||||
help_lang_t lang;
|
||||
ui_lang_t lang;
|
||||
|
||||
assert(i18n_try_parse_lang(" zh ", &lang) == true);
|
||||
assert(lang == LANG_ZH);
|
||||
assert(i18n_parse_lang("\ten_US.UTF-8\n", LANG_ZH) == LANG_EN);
|
||||
assert(i18n_try_parse_lang(" english ", &lang) == false);
|
||||
assert(i18n_try_parse_lang("zh CN", &lang) == false);
|
||||
assert(i18n_try_parse_ui_lang(" zh ", &lang) == true);
|
||||
assert(lang == UI_LANG_ZH);
|
||||
assert(i18n_parse_ui_lang("\ten_US.UTF-8\n", UI_LANG_ZH) == UI_LANG_EN);
|
||||
assert(i18n_try_parse_ui_lang(" english ", &lang) == false);
|
||||
assert(i18n_try_parse_ui_lang("zh CN", &lang) == false);
|
||||
|
||||
setenv("TNT_LANG", " zh ", 1);
|
||||
setenv("LC_ALL", "en_US.UTF-8", 1);
|
||||
assert(i18n_default_lang() == LANG_ZH);
|
||||
assert(i18n_default_ui_lang() == UI_LANG_ZH);
|
||||
}
|
||||
|
||||
TEST(default_prefers_tnt_lang) {
|
||||
setenv("TNT_LANG", "zh_CN.UTF-8", 1);
|
||||
setenv("LC_ALL", "en_US.UTF-8", 1);
|
||||
assert(i18n_default_lang() == LANG_ZH);
|
||||
assert(i18n_default_ui_lang() == UI_LANG_ZH);
|
||||
|
||||
setenv("TNT_LANG", "en", 1);
|
||||
setenv("LC_ALL", "zh_CN.UTF-8", 1);
|
||||
assert(i18n_default_lang() == LANG_EN);
|
||||
assert(i18n_default_ui_lang() == UI_LANG_EN);
|
||||
}
|
||||
|
||||
TEST(default_uses_locale_when_no_tnt_lang) {
|
||||
unsetenv("TNT_LANG");
|
||||
setenv("LC_ALL", "zh_CN.UTF-8", 1);
|
||||
assert(i18n_default_lang() == LANG_ZH);
|
||||
assert(i18n_default_ui_lang() == UI_LANG_ZH);
|
||||
|
||||
setenv("LC_ALL", "C", 1);
|
||||
assert(i18n_default_lang() == LANG_EN);
|
||||
assert(i18n_default_ui_lang() == UI_LANG_EN);
|
||||
}
|
||||
|
||||
TEST(text_lookup_matches_language) {
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_USERNAME_PROMPT),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_USERNAME_PROMPT),
|
||||
"display name") != NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_USERNAME_PROMPT),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_USERNAME_PROMPT),
|
||||
"用户名") != NULL);
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_WELCOME_SUBTITLE),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_WELCOME_SUBTITLE),
|
||||
"anonymous chat") != NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_WELCOME_SUBTITLE),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_WELCOME_SUBTITLE),
|
||||
"匿名聊天室") != NULL);
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_HELP_STATUS_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_HELP_STATUS_FORMAT),
|
||||
"KEY REFERENCE") != NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_HELP_STATUS_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_HELP_STATUS_FORMAT),
|
||||
"按键参考") != NULL);
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_COMMAND_OUTPUT_TITLE),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_COMMAND_OUTPUT_TITLE),
|
||||
"COMMAND") != NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_COMMAND_OUTPUT_TITLE),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_COMMAND_OUTPUT_TITLE),
|
||||
"命令输出") != NULL);
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_COMMAND_OUTPUT_STATUS_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_COMMAND_OUTPUT_STATUS_FORMAT),
|
||||
"q:close") != NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_COMMAND_OUTPUT_STATUS_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_COMMAND_OUTPUT_STATUS_FORMAT),
|
||||
"q:关闭") != NULL);
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_MOTD_CONTINUE_HINT),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_MOTD_CONTINUE_HINT),
|
||||
"Press any key") != NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_MOTD_CONTINUE_HINT),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_MOTD_CONTINUE_HINT),
|
||||
"按任意键") != NULL);
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_TITLE_ONLINE_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_TITLE_ONLINE_FORMAT),
|
||||
"online") != NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_TITLE_ONLINE_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_TITLE_ONLINE_FORMAT),
|
||||
"在线") != NULL);
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_IDLE_TIMEOUT_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_IDLE_TIMEOUT_FORMAT),
|
||||
"idle timeout") != NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_IDLE_TIMEOUT_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_IDLE_TIMEOUT_FORMAT),
|
||||
"空闲超时") != NULL);
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_MSG_USAGE),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_MSG_USAGE),
|
||||
"msg <username>") != NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_MSG_USAGE),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_MSG_USAGE),
|
||||
"用户名") != NULL);
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_SEARCH_HEADER_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_SEARCH_HEADER_FORMAT),
|
||||
"Search") != NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_SEARCH_HEADER_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_SEARCH_HEADER_FORMAT),
|
||||
"搜索") != NULL);
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_LANG_CURRENT_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_LANG_CURRENT_FORMAT),
|
||||
"lang <en|zh>") != NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_LANG_CURRENT_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_LANG_CURRENT_FORMAT),
|
||||
"lang <en|zh>") != NULL);
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_UNKNOWN_COMMAND_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_UNKNOWN_COMMAND_FORMAT),
|
||||
"Unknown command") != NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_UNKNOWN_COMMAND_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_UNKNOWN_COMMAND_FORMAT),
|
||||
"未知命令") != NULL);
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_EXEC_HELP),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_EXEC_HELP),
|
||||
"TNT exec interface") != NULL);
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_EXEC_HELP),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_EXEC_HELP),
|
||||
"support") == NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_EXEC_HELP),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_EXEC_HELP),
|
||||
"TNT exec 接口") != NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_EXEC_HELP),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_EXEC_HELP),
|
||||
"support") == NULL);
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_EXEC_POST_EMPTY),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_EXEC_POST_EMPTY),
|
||||
"message cannot be empty") != NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_EXEC_POST_EMPTY),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_EXEC_POST_EMPTY),
|
||||
"消息不能为空") != NULL);
|
||||
assert(strstr(i18n_text(LANG_EN, I18N_EXEC_UNKNOWN_COMMAND_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_EN, I18N_EXEC_UNKNOWN_COMMAND_FORMAT),
|
||||
"Unknown command") != NULL);
|
||||
assert(strstr(i18n_text(LANG_ZH, I18N_EXEC_UNKNOWN_COMMAND_FORMAT),
|
||||
assert(strstr(i18n_text(UI_LANG_ZH, I18N_EXEC_UNKNOWN_COMMAND_FORMAT),
|
||||
"未知命令") != NULL);
|
||||
assert(strcmp(i18n_lang_code(LANG_EN), "en") == 0);
|
||||
assert(strcmp(i18n_lang_code(LANG_ZH), "zh") == 0);
|
||||
assert(strcmp(i18n_ui_lang_code(UI_LANG_EN), "en") == 0);
|
||||
assert(strcmp(i18n_ui_lang_code(UI_LANG_ZH), "zh") == 0);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ TEST(interactive_manual_matches_language) {
|
|||
size_t en_pos = 0;
|
||||
size_t zh_pos = 0;
|
||||
|
||||
manual_text_append_interactive(en, sizeof(en), &en_pos, LANG_EN);
|
||||
manual_text_append_interactive(zh, sizeof(zh), &zh_pos, LANG_ZH);
|
||||
manual_text_append_interactive(en, sizeof(en), &en_pos, UI_LANG_EN);
|
||||
manual_text_append_interactive(zh, sizeof(zh), &zh_pos, UI_LANG_ZH);
|
||||
|
||||
assert(strstr(en, "TNT(1) help") != NULL);
|
||||
assert(strstr(en, "Use") != NULL);
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ static int tests_passed = 0;
|
|||
TEST(join_leave_follow_language) {
|
||||
message_t msg;
|
||||
|
||||
system_message_make_join(&msg, "alice", LANG_ZH);
|
||||
system_message_make_join(&msg, "alice", UI_LANG_ZH);
|
||||
assert(strcmp(msg.username, "系统") == 0);
|
||||
assert(strstr(msg.content, "alice") != NULL);
|
||||
assert(strstr(msg.content, "加入了聊天室") != NULL);
|
||||
assert(system_message_is_system(&msg));
|
||||
assert(system_message_is_join_leave(&msg));
|
||||
|
||||
system_message_make_leave(&msg, "bob", LANG_EN);
|
||||
system_message_make_leave(&msg, "bob", UI_LANG_EN);
|
||||
assert(strcmp(msg.username, "system") == 0);
|
||||
assert(strstr(msg.content, "bob") != NULL);
|
||||
assert(strstr(msg.content, "left the room") != NULL);
|
||||
|
|
@ -36,7 +36,7 @@ TEST(join_leave_follow_language) {
|
|||
TEST(nick_messages_are_system_events_not_join_leave) {
|
||||
message_t msg;
|
||||
|
||||
system_message_make_nick(&msg, "old", "new", LANG_EN);
|
||||
system_message_make_nick(&msg, "old", "new", UI_LANG_EN);
|
||||
assert(strcmp(msg.username, "system") == 0);
|
||||
assert(strstr(msg.content, "old") != NULL);
|
||||
assert(strstr(msg.content, "new") != NULL);
|
||||
|
|
@ -44,7 +44,7 @@ TEST(nick_messages_are_system_events_not_join_leave) {
|
|||
assert(system_message_is_system(&msg));
|
||||
assert(!system_message_is_join_leave(&msg));
|
||||
|
||||
system_message_make_nick(&msg, "旧", "新", LANG_ZH);
|
||||
system_message_make_nick(&msg, "旧", "新", UI_LANG_ZH);
|
||||
assert(strcmp(msg.username, "系统") == 0);
|
||||
assert(strstr(msg.content, "更名为") != NULL);
|
||||
assert(system_message_is_system(&msg));
|
||||
|
|
|
|||
Loading…
Reference in a new issue