mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 05:34:39 +08:00
i18n: split text catalog from language parsing
This commit is contained in:
parent
06a10e2df8
commit
8fbd789dfb
10 changed files with 308 additions and 277 deletions
|
|
@ -259,7 +259,8 @@ TNT/
|
|||
│ ├── help_text.c # full-screen key reference content
|
||||
│ ├── manual.c # concise manual panel rendering
|
||||
│ ├── manual_text.c # concise manual content
|
||||
│ ├── i18n.c # language selection and shared UI text
|
||||
│ ├── i18n.c # UI language and locale selection
|
||||
│ ├── i18n_text.c # shared UI text catalog
|
||||
│ ├── ratelimit.c # connection limits and rate limiting
|
||||
│ ├── tui.c # terminal UI rendering
|
||||
│ ├── tui_status.c # status/input line rendering
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
## Unreleased
|
||||
|
||||
### Changed
|
||||
- Split UI-language parsing from localized text lookup: `src/i18n.c` now owns
|
||||
locale/code parsing, while `src/i18n_text.c` owns the table-driven text
|
||||
catalog with coverage checks for every message ID.
|
||||
- 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.
|
||||
|
|
|
|||
|
|
@ -449,11 +449,11 @@ keys.
|
|||
|
||||
### Current Limitations
|
||||
|
||||
The current `src/i18n.c` implementation is a small-project translation table
|
||||
implemented in C, not a full gettext catalog. It is acceptable for two
|
||||
languages, but adding more languages should first split message lookup from
|
||||
language parsing and move toward catalog-like storage. Do not grow the
|
||||
current approach by adding ad hoc branches for every locale.
|
||||
The current `src/i18n_text.c` implementation is a small-project translation
|
||||
table implemented in C, not a full gettext catalog. It is acceptable for two
|
||||
languages because message lookup is already split from language parsing in
|
||||
`src/i18n.c`, but adding more languages should move toward catalog-like
|
||||
storage instead of adding ad hoc branches for every locale.
|
||||
|
||||
Relevant conventions:
|
||||
- POSIX locale variables: `LANG`, `LC_ALL`, `LC_MESSAGES`.
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ STRUCTURE
|
|||
src/help_text.c full-screen key reference text
|
||||
src/manual.c concise manual panel rendering
|
||||
src/manual_text.c concise manual content
|
||||
src/i18n.c language selection and shared text
|
||||
src/i18n.c UI language and locale selection
|
||||
src/i18n_text.c shared UI text catalog
|
||||
src/ratelimit.c connection limits and rate limiting
|
||||
src/tui.c rendering
|
||||
src/tui_status.c status/input line rendering
|
||||
|
|
|
|||
|
|
@ -60,7 +60,8 @@ typedef enum {
|
|||
I18N_EXEC_POST_USAGE,
|
||||
I18N_EXEC_POST_EMPTY,
|
||||
I18N_EXEC_POST_INVALID_UTF8,
|
||||
I18N_EXEC_UNKNOWN_COMMAND_FORMAT
|
||||
I18N_EXEC_UNKNOWN_COMMAND_FORMAT,
|
||||
I18N_TEXT_COUNT
|
||||
} i18n_text_id_t;
|
||||
|
||||
bool i18n_try_parse_ui_lang(const char *value, ui_lang_t *lang);
|
||||
|
|
|
|||
|
|
@ -32,5 +32,4 @@ void tui_clear_screen(struct client *client);
|
|||
* itself afterwards. */
|
||||
void tui_render_welcome(struct client *client);
|
||||
|
||||
/* Get help text based on language */
|
||||
#endif /* TUI_H */
|
||||
|
|
|
|||
266
src/i18n.c
266
src/i18n.c
|
|
@ -84,269 +84,3 @@ ui_lang_t i18n_default_ui_lang(void) {
|
|||
const char *i18n_ui_lang_code(ui_lang_t lang) {
|
||||
return lang == UI_LANG_ZH ? "zh" : "en";
|
||||
}
|
||||
|
||||
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): ";
|
||||
case I18N_INVALID_USERNAME:
|
||||
return "用户名无效,已改用 anonymous。\r\n";
|
||||
case I18N_ROOM_FULL:
|
||||
return "房间已满\r\n";
|
||||
case I18N_WELCOME_SUBTITLE:
|
||||
return "匿名聊天室 · SSH";
|
||||
case I18N_WELCOME_TAGLINE:
|
||||
return "键盘友好的终端交流";
|
||||
case I18N_WELCOME_FALLBACK_FORMAT:
|
||||
return "TNT %s - SSH 匿名聊天室\r\n\r\n";
|
||||
case I18N_INSERT_HINT_WIDE:
|
||||
return "Enter 发送 · Esc 浏览 · :help";
|
||||
case I18N_INSERT_HINT_NARROW:
|
||||
return "Enter · Esc · :help";
|
||||
case I18N_NORMAL_LATEST:
|
||||
return "G 最新";
|
||||
case I18N_NORMAL_NEW_MESSAGES:
|
||||
return "新消息";
|
||||
case I18N_HELP_TITLE:
|
||||
return " 按键 ";
|
||||
case I18N_HELP_STATUS_FORMAT:
|
||||
return "-- 按键参考 -- (%d/%d) j/k:滚动 g/G:首尾 e/z:语言 q:关闭";
|
||||
case I18N_COMMAND_OUTPUT_TITLE:
|
||||
return " 命令输出 ";
|
||||
case I18N_COMMAND_OUTPUT_STATUS_FORMAT:
|
||||
return "-- 命令输出 -- (%d/%d) j/k:滚动 Ctrl-D/U:半页 g/G:首尾 q:关闭";
|
||||
case I18N_MOTD_TITLE:
|
||||
return " 公告 ";
|
||||
case I18N_MOTD_CONTINUE_HINT:
|
||||
return " 按任意键继续 ";
|
||||
case I18N_TITLE_ONLINE_FORMAT:
|
||||
return "在线 %d";
|
||||
case I18N_TITLE_MUTED:
|
||||
return "静音";
|
||||
case I18N_TITLE_HELP_HINT:
|
||||
return "? 按键";
|
||||
case I18N_IDLE_TIMEOUT_FORMAT:
|
||||
return "\r\n\033[33m已断开: 空闲超时 (%d 分钟)\033[0m\r\n";
|
||||
case I18N_SYSTEM_USERNAME:
|
||||
return "系统";
|
||||
case I18N_SYSTEM_JOIN_FORMAT:
|
||||
return "%s 加入了聊天室";
|
||||
case I18N_SYSTEM_LEAVE_FORMAT:
|
||||
return "%s 离开了聊天室";
|
||||
case I18N_SYSTEM_NICK_FORMAT:
|
||||
return "%s 更名为 %s";
|
||||
case I18N_USERS_TITLE:
|
||||
return "在线用户";
|
||||
case I18N_MSG_USAGE:
|
||||
return "用法: msg <用户名> <消息>\n"
|
||||
" w <用户名> <消息>\n";
|
||||
case I18N_MSG_SENT_FORMAT:
|
||||
return "悄悄话已发送给 %s\n";
|
||||
case I18N_MSG_USER_NOT_FOUND_FORMAT:
|
||||
return "未找到用户 '%s'\n";
|
||||
case I18N_INBOX_TITLE:
|
||||
return "悄悄话";
|
||||
case I18N_INBOX_EMPTY:
|
||||
return "(空)";
|
||||
case I18N_NICK_USAGE:
|
||||
return "用法: nick <新用户名>\n";
|
||||
case I18N_NICK_INVALID:
|
||||
return "用户名无效\n";
|
||||
case I18N_NICK_TAKEN_FORMAT:
|
||||
return "昵称 '%s' 已被使用\n";
|
||||
case I18N_NICK_UNCHANGED:
|
||||
return "昵称未变化\n";
|
||||
case I18N_NICK_CHANGED_FORMAT:
|
||||
return "昵称已修改: %s -> %s\n";
|
||||
case I18N_LAST_USAGE:
|
||||
return "用法: last [N] (N: 1-50,默认 10)\n";
|
||||
case I18N_LAST_HEADER_FORMAT:
|
||||
return "--- 最近 %d 条消息 ---\n";
|
||||
case I18N_SEARCH_USAGE:
|
||||
return "用法: search <关键词>\n";
|
||||
case I18N_SEARCH_HEADER_FORMAT:
|
||||
return "--- 搜索: \"%s\" (%d 条匹配) ---\n";
|
||||
case I18N_MUTE_JOINS_FORMAT:
|
||||
return "加入/离开提示: %s\n";
|
||||
case I18N_MUTE_JOINS_MUTED:
|
||||
return "已静音";
|
||||
case I18N_MUTE_JOINS_UNMUTED:
|
||||
return "已开启";
|
||||
case I18N_CLEAR_DONE:
|
||||
return "命令输出已清空\n";
|
||||
case I18N_LANG_CURRENT_FORMAT:
|
||||
return "当前语言: %s\n"
|
||||
"用法: lang <en|zh>\n";
|
||||
case I18N_LANG_SET_FORMAT:
|
||||
return "语言已切换为: %s\n";
|
||||
case I18N_LANG_UNSUPPORTED_FORMAT:
|
||||
return "不支持的语言: %s\n"
|
||||
"用法: lang <en|zh>\n";
|
||||
case I18N_UNKNOWN_COMMAND_FORMAT:
|
||||
return "未知命令: %s\n";
|
||||
case I18N_DID_YOU_MEAN_FORMAT:
|
||||
return "你是想输入 :%s 吗?\n";
|
||||
case I18N_UNKNOWN_GUIDANCE:
|
||||
return "输入 :help 查看帮助\n";
|
||||
case I18N_EXEC_HELP:
|
||||
return "TNT exec 接口\n"
|
||||
"命令:\n"
|
||||
" help 显示此帮助\n"
|
||||
" health 输出服务健康状态\n"
|
||||
" users [--json] 列出在线用户\n"
|
||||
" stats [--json] 输出房间统计\n"
|
||||
" tail [N] 输出最近消息\n"
|
||||
" tail -n N 输出最近消息\n"
|
||||
" post MESSAGE 非交互发送消息\n"
|
||||
" post \"/me act\" 发送动作消息\n"
|
||||
" exit 成功退出\n";
|
||||
case I18N_EXEC_USERS_USAGE:
|
||||
return "users: 用法: users [--json]\n";
|
||||
case I18N_EXEC_STATS_USAGE:
|
||||
return "stats: 用法: stats [--json]\n";
|
||||
case I18N_EXEC_TAIL_USAGE:
|
||||
return "tail: 用法: tail [N] | tail -n N\n";
|
||||
case I18N_EXEC_POST_USAGE:
|
||||
return "post: 用法: post MESSAGE\n";
|
||||
case I18N_EXEC_POST_EMPTY:
|
||||
return "post: 消息不能为空\n";
|
||||
case I18N_EXEC_POST_INVALID_UTF8:
|
||||
return "post: 输入不是有效 UTF-8\n";
|
||||
case I18N_EXEC_UNKNOWN_COMMAND_FORMAT:
|
||||
return "未知命令: %s\n";
|
||||
}
|
||||
}
|
||||
|
||||
switch (id) {
|
||||
case I18N_USERNAME_PROMPT:
|
||||
return " Enter display name (blank for anonymous): ";
|
||||
case I18N_INVALID_USERNAME:
|
||||
return "Invalid username. Using 'anonymous' instead.\r\n";
|
||||
case I18N_ROOM_FULL:
|
||||
return "Room is full\r\n";
|
||||
case I18N_WELCOME_SUBTITLE:
|
||||
return "anonymous chat · SSH";
|
||||
case I18N_WELCOME_TAGLINE:
|
||||
return "keyboard-first terminal chat";
|
||||
case I18N_WELCOME_FALLBACK_FORMAT:
|
||||
return "TNT %s - anonymous chat over SSH\r\n\r\n";
|
||||
case I18N_INSERT_HINT_WIDE:
|
||||
return "Enter send · Esc browse · :help";
|
||||
case I18N_INSERT_HINT_NARROW:
|
||||
return "Enter · Esc · :help";
|
||||
case I18N_NORMAL_LATEST:
|
||||
return "G latest";
|
||||
case I18N_NORMAL_NEW_MESSAGES:
|
||||
return "new";
|
||||
case I18N_HELP_TITLE:
|
||||
return " KEYS ";
|
||||
case I18N_HELP_STATUS_FORMAT:
|
||||
return "-- KEY REFERENCE -- (%d/%d) j/k:scroll g/G:top/bottom e/z:lang q:close";
|
||||
case I18N_COMMAND_OUTPUT_TITLE:
|
||||
return " COMMAND OUTPUT ";
|
||||
case I18N_COMMAND_OUTPUT_STATUS_FORMAT:
|
||||
return "-- COMMAND OUTPUT -- (%d/%d) j/k:scroll Ctrl-D/U:half g/G:top/bottom q:close";
|
||||
case I18N_MOTD_TITLE:
|
||||
return " NOTICE ";
|
||||
case I18N_MOTD_CONTINUE_HINT:
|
||||
return " Press any key ";
|
||||
case I18N_TITLE_ONLINE_FORMAT:
|
||||
return "online %d";
|
||||
case I18N_TITLE_MUTED:
|
||||
return "muted";
|
||||
case I18N_TITLE_HELP_HINT:
|
||||
return "? keys";
|
||||
case I18N_IDLE_TIMEOUT_FORMAT:
|
||||
return "\r\n\033[33mDisconnected: idle timeout (%d min)\033[0m\r\n";
|
||||
case I18N_SYSTEM_USERNAME:
|
||||
return "system";
|
||||
case I18N_SYSTEM_JOIN_FORMAT:
|
||||
return "%s joined the room";
|
||||
case I18N_SYSTEM_LEAVE_FORMAT:
|
||||
return "%s left the room";
|
||||
case I18N_SYSTEM_NICK_FORMAT:
|
||||
return "%s renamed to %s";
|
||||
case I18N_USERS_TITLE:
|
||||
return "Online users";
|
||||
case I18N_MSG_USAGE:
|
||||
return "Usage: msg <username> <message>\n"
|
||||
" w <username> <message>\n";
|
||||
case I18N_MSG_SENT_FORMAT:
|
||||
return "Whisper sent to %s\n";
|
||||
case I18N_MSG_USER_NOT_FOUND_FORMAT:
|
||||
return "User '%s' not found\n";
|
||||
case I18N_INBOX_TITLE:
|
||||
return "Whispers";
|
||||
case I18N_INBOX_EMPTY:
|
||||
return "(empty)";
|
||||
case I18N_NICK_USAGE:
|
||||
return "Usage: nick <new_username>\n";
|
||||
case I18N_NICK_INVALID:
|
||||
return "Invalid username\n";
|
||||
case I18N_NICK_TAKEN_FORMAT:
|
||||
return "Nickname '%s' is already taken\n";
|
||||
case I18N_NICK_UNCHANGED:
|
||||
return "Nickname unchanged\n";
|
||||
case I18N_NICK_CHANGED_FORMAT:
|
||||
return "Nickname changed: %s -> %s\n";
|
||||
case I18N_LAST_USAGE:
|
||||
return "Usage: last [N] (N: 1-50, default 10)\n";
|
||||
case I18N_LAST_HEADER_FORMAT:
|
||||
return "--- Last %d message(s) ---\n";
|
||||
case I18N_SEARCH_USAGE:
|
||||
return "Usage: search <keyword>\n";
|
||||
case I18N_SEARCH_HEADER_FORMAT:
|
||||
return "--- Search: \"%s\" (%d match(es)) ---\n";
|
||||
case I18N_MUTE_JOINS_FORMAT:
|
||||
return "Join/leave notifications: %s\n";
|
||||
case I18N_MUTE_JOINS_MUTED:
|
||||
return "muted";
|
||||
case I18N_MUTE_JOINS_UNMUTED:
|
||||
return "unmuted";
|
||||
case I18N_CLEAR_DONE:
|
||||
return "Command output cleared\n";
|
||||
case I18N_LANG_CURRENT_FORMAT:
|
||||
return "Current language: %s\n"
|
||||
"Usage: lang <en|zh>\n";
|
||||
case I18N_LANG_SET_FORMAT:
|
||||
return "Language set to: %s\n";
|
||||
case I18N_LANG_UNSUPPORTED_FORMAT:
|
||||
return "Unsupported language: %s\n"
|
||||
"Usage: lang <en|zh>\n";
|
||||
case I18N_UNKNOWN_COMMAND_FORMAT:
|
||||
return "Unknown command: %s\n";
|
||||
case I18N_DID_YOU_MEAN_FORMAT:
|
||||
return "Did you mean :%s?\n";
|
||||
case I18N_UNKNOWN_GUIDANCE:
|
||||
return "Type :help for help\n";
|
||||
case I18N_EXEC_HELP:
|
||||
return "TNT exec interface\n"
|
||||
"Commands:\n"
|
||||
" help Show this help\n"
|
||||
" health Print service health\n"
|
||||
" users [--json] List online users\n"
|
||||
" stats [--json] Print room statistics\n"
|
||||
" tail [N] Print recent messages\n"
|
||||
" tail -n N Print recent messages\n"
|
||||
" post MESSAGE Post a message non-interactively\n"
|
||||
" post \"/me act\" Post an action message\n"
|
||||
" exit Exit successfully\n";
|
||||
case I18N_EXEC_USERS_USAGE:
|
||||
return "users: usage: users [--json]\n";
|
||||
case I18N_EXEC_STATS_USAGE:
|
||||
return "stats: usage: stats [--json]\n";
|
||||
case I18N_EXEC_TAIL_USAGE:
|
||||
return "tail: usage: tail [N] | tail -n N\n";
|
||||
case I18N_EXEC_POST_USAGE:
|
||||
return "post: usage: post MESSAGE\n";
|
||||
case I18N_EXEC_POST_EMPTY:
|
||||
return "post: message cannot be empty\n";
|
||||
case I18N_EXEC_POST_INVALID_UTF8:
|
||||
return "post: invalid UTF-8 input\n";
|
||||
case I18N_EXEC_UNKNOWN_COMMAND_FORMAT:
|
||||
return "Unknown command: %s\n";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
|
|
|||
278
src/i18n_text.c
Normal file
278
src/i18n_text.c
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
#include "i18n.h"
|
||||
|
||||
typedef struct {
|
||||
const char *en;
|
||||
const char *zh;
|
||||
} i18n_text_entry_t;
|
||||
|
||||
static const i18n_text_entry_t text_catalog[I18N_TEXT_COUNT] = {
|
||||
[I18N_USERNAME_PROMPT] = {
|
||||
" Enter display name (blank for anonymous): ",
|
||||
" 请输入用户名 (留空 anonymous): "
|
||||
},
|
||||
[I18N_INVALID_USERNAME] = {
|
||||
"Invalid username. Using 'anonymous' instead.\r\n",
|
||||
"用户名无效,已改用 anonymous。\r\n"
|
||||
},
|
||||
[I18N_ROOM_FULL] = {
|
||||
"Room is full\r\n",
|
||||
"房间已满\r\n"
|
||||
},
|
||||
[I18N_WELCOME_SUBTITLE] = {
|
||||
"anonymous chat · SSH",
|
||||
"匿名聊天室 · SSH"
|
||||
},
|
||||
[I18N_WELCOME_TAGLINE] = {
|
||||
"keyboard-first terminal chat",
|
||||
"键盘友好的终端交流"
|
||||
},
|
||||
[I18N_WELCOME_FALLBACK_FORMAT] = {
|
||||
"TNT %s - anonymous chat over SSH\r\n\r\n",
|
||||
"TNT %s - SSH 匿名聊天室\r\n\r\n"
|
||||
},
|
||||
[I18N_INSERT_HINT_WIDE] = {
|
||||
"Enter send · Esc browse · :help",
|
||||
"Enter 发送 · Esc 浏览 · :help"
|
||||
},
|
||||
[I18N_INSERT_HINT_NARROW] = {
|
||||
"Enter · Esc · :help",
|
||||
"Enter · Esc · :help"
|
||||
},
|
||||
[I18N_NORMAL_LATEST] = {
|
||||
"G latest",
|
||||
"G 最新"
|
||||
},
|
||||
[I18N_NORMAL_NEW_MESSAGES] = {
|
||||
"new",
|
||||
"新消息"
|
||||
},
|
||||
[I18N_HELP_TITLE] = {
|
||||
" KEYS ",
|
||||
" 按键 "
|
||||
},
|
||||
[I18N_HELP_STATUS_FORMAT] = {
|
||||
"-- KEY REFERENCE -- (%d/%d) j/k:scroll g/G:top/bottom e/z:lang q:close",
|
||||
"-- 按键参考 -- (%d/%d) j/k:滚动 g/G:首尾 e/z:语言 q:关闭"
|
||||
},
|
||||
[I18N_COMMAND_OUTPUT_TITLE] = {
|
||||
" COMMAND OUTPUT ",
|
||||
" 命令输出 "
|
||||
},
|
||||
[I18N_COMMAND_OUTPUT_STATUS_FORMAT] = {
|
||||
"-- COMMAND OUTPUT -- (%d/%d) j/k:scroll Ctrl-D/U:half g/G:top/bottom q:close",
|
||||
"-- 命令输出 -- (%d/%d) j/k:滚动 Ctrl-D/U:半页 g/G:首尾 q:关闭"
|
||||
},
|
||||
[I18N_MOTD_TITLE] = {
|
||||
" NOTICE ",
|
||||
" 公告 "
|
||||
},
|
||||
[I18N_MOTD_CONTINUE_HINT] = {
|
||||
" Press any key ",
|
||||
" 按任意键继续 "
|
||||
},
|
||||
[I18N_TITLE_ONLINE_FORMAT] = {
|
||||
"online %d",
|
||||
"在线 %d"
|
||||
},
|
||||
[I18N_TITLE_MUTED] = {
|
||||
"muted",
|
||||
"静音"
|
||||
},
|
||||
[I18N_TITLE_HELP_HINT] = {
|
||||
"? keys",
|
||||
"? 按键"
|
||||
},
|
||||
[I18N_IDLE_TIMEOUT_FORMAT] = {
|
||||
"\r\n\033[33mDisconnected: idle timeout (%d min)\033[0m\r\n",
|
||||
"\r\n\033[33m已断开: 空闲超时 (%d 分钟)\033[0m\r\n"
|
||||
},
|
||||
[I18N_SYSTEM_USERNAME] = {
|
||||
"system",
|
||||
"系统"
|
||||
},
|
||||
[I18N_SYSTEM_JOIN_FORMAT] = {
|
||||
"%s joined the room",
|
||||
"%s 加入了聊天室"
|
||||
},
|
||||
[I18N_SYSTEM_LEAVE_FORMAT] = {
|
||||
"%s left the room",
|
||||
"%s 离开了聊天室"
|
||||
},
|
||||
[I18N_SYSTEM_NICK_FORMAT] = {
|
||||
"%s renamed to %s",
|
||||
"%s 更名为 %s"
|
||||
},
|
||||
[I18N_USERS_TITLE] = {
|
||||
"Online users",
|
||||
"在线用户"
|
||||
},
|
||||
[I18N_MSG_USAGE] = {
|
||||
"Usage: msg <username> <message>\n"
|
||||
" w <username> <message>\n",
|
||||
"用法: msg <用户名> <消息>\n"
|
||||
" w <用户名> <消息>\n"
|
||||
},
|
||||
[I18N_MSG_SENT_FORMAT] = {
|
||||
"Whisper sent to %s\n",
|
||||
"悄悄话已发送给 %s\n"
|
||||
},
|
||||
[I18N_MSG_USER_NOT_FOUND_FORMAT] = {
|
||||
"User '%s' not found\n",
|
||||
"未找到用户 '%s'\n"
|
||||
},
|
||||
[I18N_INBOX_TITLE] = {
|
||||
"Whispers",
|
||||
"悄悄话"
|
||||
},
|
||||
[I18N_INBOX_EMPTY] = {
|
||||
"(empty)",
|
||||
"(空)"
|
||||
},
|
||||
[I18N_NICK_USAGE] = {
|
||||
"Usage: nick <new_username>\n",
|
||||
"用法: nick <新用户名>\n"
|
||||
},
|
||||
[I18N_NICK_INVALID] = {
|
||||
"Invalid username\n",
|
||||
"用户名无效\n"
|
||||
},
|
||||
[I18N_NICK_TAKEN_FORMAT] = {
|
||||
"Nickname '%s' is already taken\n",
|
||||
"昵称 '%s' 已被使用\n"
|
||||
},
|
||||
[I18N_NICK_UNCHANGED] = {
|
||||
"Nickname unchanged\n",
|
||||
"昵称未变化\n"
|
||||
},
|
||||
[I18N_NICK_CHANGED_FORMAT] = {
|
||||
"Nickname changed: %s -> %s\n",
|
||||
"昵称已修改: %s -> %s\n"
|
||||
},
|
||||
[I18N_LAST_USAGE] = {
|
||||
"Usage: last [N] (N: 1-50, default 10)\n",
|
||||
"用法: last [N] (N: 1-50,默认 10)\n"
|
||||
},
|
||||
[I18N_LAST_HEADER_FORMAT] = {
|
||||
"--- Last %d message(s) ---\n",
|
||||
"--- 最近 %d 条消息 ---\n"
|
||||
},
|
||||
[I18N_SEARCH_USAGE] = {
|
||||
"Usage: search <keyword>\n",
|
||||
"用法: search <关键词>\n"
|
||||
},
|
||||
[I18N_SEARCH_HEADER_FORMAT] = {
|
||||
"--- Search: \"%s\" (%d match(es)) ---\n",
|
||||
"--- 搜索: \"%s\" (%d 条匹配) ---\n"
|
||||
},
|
||||
[I18N_MUTE_JOINS_FORMAT] = {
|
||||
"Join/leave notifications: %s\n",
|
||||
"加入/离开提示: %s\n"
|
||||
},
|
||||
[I18N_MUTE_JOINS_MUTED] = {
|
||||
"muted",
|
||||
"已静音"
|
||||
},
|
||||
[I18N_MUTE_JOINS_UNMUTED] = {
|
||||
"unmuted",
|
||||
"已开启"
|
||||
},
|
||||
[I18N_CLEAR_DONE] = {
|
||||
"Command output cleared\n",
|
||||
"命令输出已清空\n"
|
||||
},
|
||||
[I18N_LANG_CURRENT_FORMAT] = {
|
||||
"Current language: %s\n"
|
||||
"Usage: lang <en|zh>\n",
|
||||
"当前语言: %s\n"
|
||||
"用法: lang <en|zh>\n"
|
||||
},
|
||||
[I18N_LANG_SET_FORMAT] = {
|
||||
"Language set to: %s\n",
|
||||
"语言已切换为: %s\n"
|
||||
},
|
||||
[I18N_LANG_UNSUPPORTED_FORMAT] = {
|
||||
"Unsupported language: %s\n"
|
||||
"Usage: lang <en|zh>\n",
|
||||
"不支持的语言: %s\n"
|
||||
"用法: lang <en|zh>\n"
|
||||
},
|
||||
[I18N_UNKNOWN_COMMAND_FORMAT] = {
|
||||
"Unknown command: %s\n",
|
||||
"未知命令: %s\n"
|
||||
},
|
||||
[I18N_DID_YOU_MEAN_FORMAT] = {
|
||||
"Did you mean :%s?\n",
|
||||
"你是想输入 :%s 吗?\n"
|
||||
},
|
||||
[I18N_UNKNOWN_GUIDANCE] = {
|
||||
"Type :help for help\n",
|
||||
"输入 :help 查看帮助\n"
|
||||
},
|
||||
[I18N_EXEC_HELP] = {
|
||||
"TNT exec interface\n"
|
||||
"Commands:\n"
|
||||
" help Show this help\n"
|
||||
" health Print service health\n"
|
||||
" users [--json] List online users\n"
|
||||
" stats [--json] Print room statistics\n"
|
||||
" tail [N] Print recent messages\n"
|
||||
" tail -n N Print recent messages\n"
|
||||
" post MESSAGE Post a message non-interactively\n"
|
||||
" post \"/me act\" Post an action message\n"
|
||||
" exit Exit successfully\n",
|
||||
"TNT exec 接口\n"
|
||||
"命令:\n"
|
||||
" help 显示此帮助\n"
|
||||
" health 输出服务健康状态\n"
|
||||
" users [--json] 列出在线用户\n"
|
||||
" stats [--json] 输出房间统计\n"
|
||||
" tail [N] 输出最近消息\n"
|
||||
" tail -n N 输出最近消息\n"
|
||||
" post MESSAGE 非交互发送消息\n"
|
||||
" post \"/me act\" 发送动作消息\n"
|
||||
" exit 成功退出\n"
|
||||
},
|
||||
[I18N_EXEC_USERS_USAGE] = {
|
||||
"users: usage: users [--json]\n",
|
||||
"users: 用法: users [--json]\n"
|
||||
},
|
||||
[I18N_EXEC_STATS_USAGE] = {
|
||||
"stats: usage: stats [--json]\n",
|
||||
"stats: 用法: stats [--json]\n"
|
||||
},
|
||||
[I18N_EXEC_TAIL_USAGE] = {
|
||||
"tail: usage: tail [N] | tail -n N\n",
|
||||
"tail: 用法: tail [N] | tail -n N\n"
|
||||
},
|
||||
[I18N_EXEC_POST_USAGE] = {
|
||||
"post: usage: post MESSAGE\n",
|
||||
"post: 用法: post MESSAGE\n"
|
||||
},
|
||||
[I18N_EXEC_POST_EMPTY] = {
|
||||
"post: message cannot be empty\n",
|
||||
"post: 消息不能为空\n"
|
||||
},
|
||||
[I18N_EXEC_POST_INVALID_UTF8] = {
|
||||
"post: invalid UTF-8 input\n",
|
||||
"post: 输入不是有效 UTF-8\n"
|
||||
},
|
||||
[I18N_EXEC_UNKNOWN_COMMAND_FORMAT] = {
|
||||
"Unknown command: %s\n",
|
||||
"未知命令: %s\n"
|
||||
}
|
||||
};
|
||||
|
||||
const char *i18n_text(ui_lang_t lang, i18n_text_id_t id) {
|
||||
if (id < 0 || id >= I18N_TEXT_COUNT) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const i18n_text_entry_t *entry = &text_catalog[id];
|
||||
if (lang == UI_LANG_ZH && entry->zh) {
|
||||
return entry->zh;
|
||||
}
|
||||
if (entry->en) {
|
||||
return entry->en;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ CLI_TEXT_SRC = ../../src/cli_text.c
|
|||
CHAT_ROOM_SRC = ../../src/chat_room.c
|
||||
HISTORY_VIEW_SRC = ../../src/history_view.c
|
||||
I18N_SRC = ../../src/i18n.c
|
||||
I18N_TEXT_SRC = ../../src/i18n_text.c
|
||||
SYSTEM_MESSAGE_SRC = ../../src/system_message.c
|
||||
HELP_TEXT_SRC = ../../src/help_text.c
|
||||
MANUAL_TEXT_SRC = ../../src/manual_text.c
|
||||
|
|
@ -41,10 +42,10 @@ test_chat_room: test_chat_room.c $(CHAT_ROOM_SRC) $(MESSAGE_SRC) $(UTF8_SRC) $(C
|
|||
test_history_view: test_history_view.c $(HISTORY_VIEW_SRC)
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
test_i18n: test_i18n.c $(I18N_SRC)
|
||||
test_i18n: test_i18n.c $(I18N_SRC) $(I18N_TEXT_SRC)
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
test_system_message: test_system_message.c $(SYSTEM_MESSAGE_SRC) $(I18N_SRC)
|
||||
test_system_message: test_system_message.c $(SYSTEM_MESSAGE_SRC) $(I18N_SRC) $(I18N_TEXT_SRC)
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
test_command_catalog: test_command_catalog.c $(COMMAND_CATALOG_SRC) $(COMMON_SRC)
|
||||
|
|
|
|||
|
|
@ -146,6 +146,18 @@ TEST(text_lookup_matches_language) {
|
|||
assert(strcmp(i18n_ui_lang_code(UI_LANG_ZH), "zh") == 0);
|
||||
}
|
||||
|
||||
TEST(text_catalog_is_complete) {
|
||||
for (int id = 0; id < I18N_TEXT_COUNT; id++) {
|
||||
assert(i18n_text(UI_LANG_EN, (i18n_text_id_t)id)[0] != '\0');
|
||||
assert(i18n_text(UI_LANG_ZH, (i18n_text_id_t)id)[0] != '\0');
|
||||
}
|
||||
|
||||
assert(strcmp(i18n_text(UI_LANG_EN,
|
||||
(i18n_text_id_t)I18N_TEXT_COUNT), "") == 0);
|
||||
assert(strcmp(i18n_text(UI_LANG_ZH,
|
||||
(i18n_text_id_t)I18N_TEXT_COUNT), "") == 0);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
printf("Running i18n unit tests...\n\n");
|
||||
|
||||
|
|
@ -155,6 +167,7 @@ int main(void) {
|
|||
RUN_TEST(default_prefers_tnt_lang);
|
||||
RUN_TEST(default_uses_locale_when_no_tnt_lang);
|
||||
RUN_TEST(text_lookup_matches_language);
|
||||
RUN_TEST(text_catalog_is_complete);
|
||||
|
||||
printf("\n✓ All %d tests passed!\n", tests_passed);
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Reference in a new issue