mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 09:54:37 +08:00
116 lines
2.9 KiB
C
116 lines
2.9 KiB
C
#include "i18n.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
typedef struct {
|
|
ui_lang_t lang;
|
|
const char *code;
|
|
const char *prefixes[4];
|
|
} ui_lang_definition_t;
|
|
|
|
static const ui_lang_definition_t ui_lang_defs[] = {
|
|
{UI_LANG_EN, "en", {"en", "c", "posix", 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) {
|
|
while (value && *value &&
|
|
isspace((unsigned char)*value)) {
|
|
value++;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static bool is_lang_boundary(const char *value) {
|
|
if (*value == '\0' || *value == '_' || *value == '-' || *value == '.') {
|
|
return true;
|
|
}
|
|
if (!isspace((unsigned char)*value)) {
|
|
return false;
|
|
}
|
|
return *skip_space(value) == '\0';
|
|
}
|
|
|
|
static bool starts_with_lang(const char *value, const char *prefix) {
|
|
if (!value || !prefix) return false;
|
|
|
|
value = skip_space(value);
|
|
while (*prefix) {
|
|
if (tolower((unsigned char)*value) !=
|
|
tolower((unsigned char)*prefix)) {
|
|
return false;
|
|
}
|
|
value++;
|
|
prefix++;
|
|
}
|
|
|
|
return is_lang_boundary(value);
|
|
}
|
|
|
|
bool i18n_try_parse_ui_lang(const char *value, ui_lang_t *lang) {
|
|
if (!value || value[0] == '\0') {
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < sizeof(ui_lang_defs) / sizeof(ui_lang_defs[0]); i++) {
|
|
for (size_t j = 0; ui_lang_defs[i].prefixes[j]; j++) {
|
|
if (starts_with_lang(value, ui_lang_defs[i].prefixes[j])) {
|
|
if (lang) {
|
|
*lang = ui_lang_defs[i].lang;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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_ui_lang(explicit_lang, UI_LANG_EN);
|
|
}
|
|
|
|
const char *locale = getenv("LC_ALL");
|
|
if (!locale || locale[0] == '\0') {
|
|
locale = getenv("LC_MESSAGES");
|
|
}
|
|
if (!locale || locale[0] == '\0') {
|
|
locale = getenv("LANG");
|
|
}
|
|
|
|
return i18n_parse_ui_lang(locale, UI_LANG_EN);
|
|
}
|
|
|
|
ui_lang_t i18n_next_ui_lang(ui_lang_t lang) {
|
|
size_t count = sizeof(ui_lang_defs) / sizeof(ui_lang_defs[0]);
|
|
|
|
for (size_t i = 0; i < count; i++) {
|
|
if (ui_lang_defs[i].lang == lang) {
|
|
return ui_lang_defs[(i + 1) % count].lang;
|
|
}
|
|
}
|
|
|
|
return ui_lang_defs[0].lang;
|
|
}
|
|
|
|
const char *i18n_ui_lang_code(ui_lang_t lang) {
|
|
for (size_t i = 0; i < sizeof(ui_lang_defs) / sizeof(ui_lang_defs[0]); i++) {
|
|
if (ui_lang_defs[i].lang == lang) {
|
|
return ui_lang_defs[i].code;
|
|
}
|
|
}
|
|
|
|
return ui_lang_defs[0].code;
|
|
}
|