i18n: index text catalog by language

This commit is contained in:
m1ngsama 2026-05-24 15:20:01 +08:00
parent 69d3b76512
commit 46f5780057
5 changed files with 18 additions and 8 deletions

View file

@ -52,6 +52,8 @@
one internal language registry. one internal language registry.
- Removed the unused `MODE_HELP` enum value and refreshed development-guide - Removed the unused `MODE_HELP` enum value and refreshed development-guide
module descriptions for the split between language parsing and text lookup. module descriptions for the split between language parsing and text lookup.
- `i18n_text` now indexes localized strings by `UI_LANG_*` instead of storing
English/Chinese as hard-coded struct fields.
- Documented i18n and user-facing text rules for English-first source text, - Documented i18n and user-facing text rules for English-first source text,
stable command syntax, concise help copy, and translation-only localization. stable command syntax, concise help copy, and translation-only localization.

View file

@ -46,7 +46,8 @@ typedef enum {
/* UI language */ /* UI language */
typedef enum { typedef enum {
UI_LANG_EN, UI_LANG_EN,
UI_LANG_ZH UI_LANG_ZH,
UI_LANG_COUNT
} ui_lang_t; } ui_lang_t;
/* Runtime helpers */ /* Runtime helpers */

View file

@ -12,6 +12,8 @@ static const ui_lang_definition_t ui_lang_defs[] = {
{UI_LANG_EN, "en", {"en", "c", "posix", NULL}}, {UI_LANG_EN, "en", {"en", "c", "posix", NULL}},
{UI_LANG_ZH, "zh", {"zh", NULL}} {UI_LANG_ZH, "zh", {"zh", NULL}}
}; };
typedef char ui_lang_defs_must_cover_enum[
sizeof(ui_lang_defs) / sizeof(ui_lang_defs[0]) == UI_LANG_COUNT ? 1 : -1];
static const char *skip_space(const char *value) { static const char *skip_space(const char *value) {
while (value && *value && while (value && *value &&

View file

@ -1,8 +1,7 @@
#include "i18n.h" #include "i18n.h"
typedef struct { typedef struct {
const char *en; const char *text[UI_LANG_COUNT];
const char *zh;
} i18n_text_entry_t; } i18n_text_entry_t;
static const i18n_text_entry_t text_catalog[I18N_TEXT_COUNT] = { static const i18n_text_entry_t text_catalog[I18N_TEXT_COUNT] = {
@ -209,12 +208,16 @@ const char *i18n_text(ui_lang_t lang, i18n_text_id_t id) {
return ""; return "";
} }
const i18n_text_entry_t *entry = &text_catalog[id]; if ((int)lang < 0 || lang >= UI_LANG_COUNT) {
if (lang == UI_LANG_ZH && entry->zh) { lang = UI_LANG_EN;
return entry->zh;
} }
if (entry->en) {
return entry->en; const i18n_text_entry_t *entry = &text_catalog[id];
if (entry->text[lang]) {
return entry->text[lang];
}
if (entry->text[UI_LANG_EN]) {
return entry->text[UI_LANG_EN];
} }
return ""; return "";
} }

View file

@ -83,6 +83,8 @@ TEST(text_lookup_matches_language) {
"display name") != NULL); "display name") != NULL);
assert(strstr(i18n_text(UI_LANG_ZH, I18N_USERNAME_PROMPT), assert(strstr(i18n_text(UI_LANG_ZH, I18N_USERNAME_PROMPT),
"用户名") != NULL); "用户名") != NULL);
assert(strstr(i18n_text((ui_lang_t)99, I18N_USERNAME_PROMPT),
"display name") != NULL);
assert(strstr(i18n_text(UI_LANG_EN, I18N_WELCOME_SUBTITLE), assert(strstr(i18n_text(UI_LANG_EN, I18N_WELCOME_SUBTITLE),
"anonymous chat") != NULL); "anonymous chat") != NULL);
assert(strstr(i18n_text(UI_LANG_ZH, I18N_WELCOME_SUBTITLE), assert(strstr(i18n_text(UI_LANG_ZH, I18N_WELCOME_SUBTITLE),