From: Hirohito Higashi Date: Mon, 27 Jul 2026 22:04:06 +0000 (+0000) Subject: patch 9.2.0867: MS-Windows: messages are not in the display language X-Git-Tag: v9.2.0867^0 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=930dccfed0617979b836dfefbbe57d5c26a6679d;p=thirdparty%2Fvim.git patch 9.2.0867: MS-Windows: messages are not in the display language Problem: On MS-Windows the messages use the language of the regional format, not the language that Windows is displayed in (Markus Nißl) Solution: Use the display language for the messages, with the other preferred languages as fall back. Make ":language messages" overrule it (Hirohito Higashi). fixes: #20855 closes: #20860 Co-Authored-By: Claude Opus 5 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- diff --git a/runtime/doc/mlang.txt b/runtime/doc/mlang.txt index 6f0bb3fe62..0c1747eed6 100644 --- a/runtime/doc/mlang.txt +++ b/runtime/doc/mlang.txt @@ -1,4 +1,4 @@ -*mlang.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*mlang.txt* For Vim version 9.2. Last change: 2026 Jul 28 VIM REFERENCE MANUAL by Bram Moolenaar @@ -122,6 +122,10 @@ If you write your own translations you need to generate the .po file and convert it to a .mo file. You need to get the source distribution and read the file "src/po/README.txt". +The language of the messages comes from the display language of Windows, not +from the regional format. The other locale categories, such as the one used +for |strftime()|, do follow the regional format. + To overrule the automatic choice of the language, set the $LANG variable to the language of your choice. use "en" to disable translations. > diff --git a/src/locale.c b/src/locale.c index eb1845fce5..0d217fd434 100644 --- a/src/locale.c +++ b/src/locale.c @@ -116,10 +116,12 @@ get_mess_lang(void) p = get_locale_val(LC_MESSAGES); # else // This is necessary for Win32, where LC_MESSAGES is not defined and $LANG - // may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME - // and LC_MONETARY may be set differently for a Japanese working in the - // US. - p = get_locale_val(LC_COLLATE); + // may be set to the LCID number. $LANG holds the display language, see + // set_init_lang_env(). Otherwise LC_COLLATE is the best guess, LC_TIME + // and LC_MONETARY may be set differently for a Japanese working in the US. + p = mch_getenv((char_u *)"LANG"); + if (!is_valid_mess_lang(p)) + p = get_locale_val(LC_COLLATE); # endif # else p = mch_getenv((char_u *)"LC_ALL"); @@ -365,9 +367,6 @@ ex_language(exarg_T *eap) if (what == LC_ALL) { vim_setenv((char_u *)"LANG", name); - - // Clear $LANGUAGE because GNU gettext uses it. - vim_setenv((char_u *)"LANGUAGE", (char_u *)""); # ifdef MSWIN // Apparently MS-Windows printf() may cause a crash when // we give it 8-bit text while it's expecting text in the @@ -383,6 +382,8 @@ ex_language(exarg_T *eap) # else mname = name; # endif + // Clear $LANGUAGE because GNU gettext uses it. + vim_setenv((char_u *)"LANGUAGE", (char_u *)""); vim_setenv((char_u *)"LC_MESSAGES", mname); # ifdef FEAT_MULTI_LANG set_helplang_default(mname); diff --git a/src/option.c b/src/option.c index d4f63203dd..f511b8ea5d 100644 --- a/src/option.c +++ b/src/option.c @@ -494,6 +494,64 @@ set_init_expand_env(void) } } +#if defined(MSWIN) && defined(FEAT_GETTEXT) +/* + * Get the display language of Windows and the languages to fall back on, as a + * colon separated list for gettext, e.g. "ja_JP:en_US". The list stops after + * English, untranslated messages are English already. + * Returns NULL when it cannot be obtained. The result must be freed. + */ + static char_u * +get_ui_langs(void) +{ + ULONG num_languages = 0; + ULONG bufsize = 0; + WCHAR *buffer; + char_u *langs = NULL; + + if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &num_languages, NULL, + &bufsize) + || bufsize == 0) + return NULL; + + buffer = ALLOC_MULT(WCHAR, bufsize); + if (buffer == NULL) + return NULL; + + if (GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &num_languages, buffer, + &bufsize)) + { + // The list is NUL separated, the result needs the same room. + langs = alloc(bufsize); + if (langs != NULL) + { + char_u *d = langs; + WCHAR *s = buffer; + + while (*s != L'\0') + { + bool english = s[0] == L'e' && s[1] == L'n' + && (s[2] == L'\0' || s[2] == L'-'); + + if (d > langs) + *d++ = ':'; + // Locale names are ASCII, "en-US" becomes "en_US". + for ( ; *s != L'\0'; ++s) + *d++ = *s == L'-' ? '_' : (char_u)*s; + ++s; + + if (english) + break; + } + *d = NUL; + } + } + vim_free(buffer); + + return langs; +} +#endif + /* * Initialize the 'LANG' environment variable to a default value. */ @@ -501,32 +559,27 @@ set_init_expand_env(void) set_init_lang_env(void) { #if defined(MSWIN) && defined(FEAT_GETTEXT) - // If $LANG isn't set, try to get a good value for it. This makes the - // right language be used automatically. Don't do this for English. - if (mch_getenv((char_u *)"LANG") == NULL) - { - char buf[20]; - long_u n; - - // Could use LOCALE_SISO639LANGNAME, but it's not in Win95. - // LOCALE_SABBREVLANGNAME gives us three letters, like "enu", we use - // only the first two. - n = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, - (LPTSTR)buf, 20); - if (n >= 2 && STRNICMP(buf, "en", 2) != 0) - { - // There are a few exceptions (probably more) - if (STRNICMP(buf, "cht", 3) == 0 || STRNICMP(buf, "zht", 3) == 0) - STRCPY(buf, "zh_TW"); - else if (STRNICMP(buf, "chs", 3) == 0 - || STRNICMP(buf, "zhc", 3) == 0) - STRCPY(buf, "zh_CN"); - else if (STRNICMP(buf, "jp", 2) == 0) - STRCPY(buf, "ja"); - else - buf[2] = NUL; // truncate to two-letter code - vim_setenv((char_u *)"LANG", (char_u *)buf); + // If the language isn't set in the environment, use the display language + // of Windows. Not the regional format, which is what the CRT would use + // for setlocale(LC_ALL, ""). + if (mch_getenv((char_u *)"LANG") == NULL + && mch_getenv((char_u *)"LANGUAGE") == NULL + && mch_getenv((char_u *)"LC_ALL") == NULL + && mch_getenv((char_u *)"LC_MESSAGES") == NULL) + { + char_u *langs = get_ui_langs(); + + if (langs != NULL && *langs != NUL) + { + char_u *colon = vim_strchr(langs, ':'); + + // $LANGUAGE is the list gettext picks from, $LANG the language. + vim_setenv((char_u *)"LANGUAGE", langs); + if (colon != NULL) + *colon = NUL; + vim_setenv((char_u *)"LANG", langs); } + vim_free(langs); } #elif defined(MACOS_CONVERT) // Moved to os_mac_conv.c to avoid dependency problems. diff --git a/src/version.c b/src/version.c index 48389e931f..af42c7cbd1 100644 --- a/src/version.c +++ b/src/version.c @@ -758,6 +758,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 867, /**/ 866, /**/