mirror of
https://oauth2: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@github.com/m1ngsama/TNT.git
synced 2026-06-26 12:04:38 +08:00
i18n: tolerate whitespace in language parsing
This commit is contained in:
parent
1897a980d5
commit
86e1ec8e32
3 changed files with 38 additions and 1 deletions
|
|
@ -64,6 +64,8 @@
|
||||||
concurrent-session and connection-rate scenarios.
|
concurrent-session and connection-rate scenarios.
|
||||||
- CI memory-leak smoke checks now use an isolated state directory, wait for
|
- CI memory-leak smoke checks now use an isolated state directory, wait for
|
||||||
real SSH readiness, and clean up the exact server PID instead of `pkill`.
|
real SSH readiness, and clean up the exact server PID instead of `pkill`.
|
||||||
|
- Language parsing now tolerates surrounding whitespace and accepts the
|
||||||
|
`english` alias, improving `TNT_LANG` and `:lang` ergonomics.
|
||||||
- Refreshed README and quick-reference module maps to match the current
|
- Refreshed README and quick-reference module maps to match the current
|
||||||
`cli_text`, `help_text`, `support_text`, i18n, exec, and rate-limit modules.
|
`cli_text`, `help_text`, `support_text`, i18n, exec, and rate-limit modules.
|
||||||
- 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
|
||||||
|
|
|
||||||
22
src/i18n.c
22
src/i18n.c
|
|
@ -2,9 +2,28 @@
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
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) {
|
static bool starts_with_lang(const char *value, const char *prefix) {
|
||||||
if (!value || !prefix) return false;
|
if (!value || !prefix) return false;
|
||||||
|
|
||||||
|
value = skip_space(value);
|
||||||
while (*prefix) {
|
while (*prefix) {
|
||||||
if (tolower((unsigned char)*value) !=
|
if (tolower((unsigned char)*value) !=
|
||||||
tolower((unsigned char)*prefix)) {
|
tolower((unsigned char)*prefix)) {
|
||||||
|
|
@ -14,7 +33,7 @@ static bool starts_with_lang(const char *value, const char *prefix) {
|
||||||
prefix++;
|
prefix++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return *value == '\0' || *value == '_' || *value == '-' || *value == '.';
|
return is_lang_boundary(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool i18n_try_parse_lang(const char *value, help_lang_t *lang) {
|
bool i18n_try_parse_lang(const char *value, help_lang_t *lang) {
|
||||||
|
|
@ -30,6 +49,7 @@ bool i18n_try_parse_lang(const char *value, help_lang_t *lang) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (starts_with_lang(value, "en") ||
|
if (starts_with_lang(value, "en") ||
|
||||||
|
starts_with_lang(value, "english") ||
|
||||||
starts_with_lang(value, "c") ||
|
starts_with_lang(value, "c") ||
|
||||||
starts_with_lang(value, "posix")) {
|
starts_with_lang(value, "posix")) {
|
||||||
if (lang) *lang = LANG_EN;
|
if (lang) *lang = LANG_EN;
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,20 @@ TEST(parse_unknown_uses_fallback) {
|
||||||
assert(i18n_parse_lang("fr_FR.UTF-8", LANG_ZH) == LANG_ZH);
|
assert(i18n_parse_lang("fr_FR.UTF-8", LANG_ZH) == LANG_ZH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(parse_ignores_surrounding_whitespace) {
|
||||||
|
help_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_parse_lang(" english ", LANG_ZH) == LANG_EN);
|
||||||
|
assert(i18n_try_parse_lang("zh CN", &lang) == false);
|
||||||
|
|
||||||
|
setenv("TNT_LANG", " zh ", 1);
|
||||||
|
setenv("LC_ALL", "en_US.UTF-8", 1);
|
||||||
|
assert(i18n_default_lang() == LANG_ZH);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(default_prefers_tnt_lang) {
|
TEST(default_prefers_tnt_lang) {
|
||||||
setenv("TNT_LANG", "zh_CN.UTF-8", 1);
|
setenv("TNT_LANG", "zh_CN.UTF-8", 1);
|
||||||
setenv("LC_ALL", "en_US.UTF-8", 1);
|
setenv("LC_ALL", "en_US.UTF-8", 1);
|
||||||
|
|
@ -123,6 +137,7 @@ int main(void) {
|
||||||
|
|
||||||
RUN_TEST(parse_explicit_languages);
|
RUN_TEST(parse_explicit_languages);
|
||||||
RUN_TEST(parse_unknown_uses_fallback);
|
RUN_TEST(parse_unknown_uses_fallback);
|
||||||
|
RUN_TEST(parse_ignores_surrounding_whitespace);
|
||||||
RUN_TEST(default_prefers_tnt_lang);
|
RUN_TEST(default_prefers_tnt_lang);
|
||||||
RUN_TEST(default_uses_locale_when_no_tnt_lang);
|
RUN_TEST(default_uses_locale_when_no_tnt_lang);
|
||||||
RUN_TEST(text_lookup_matches_language);
|
RUN_TEST(text_lookup_matches_language);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue