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) <noreply@anthropic.com>
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Hirohito HigashiandClaude Opus 5 authored and Christian Brabandt committed 2026-07-27 22:07:22 +00:00
1 parent a29d16a391
commit 930dccfed0
4 files changed
+90 -30

No files matched your search

+5 -1
View File
@@ -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. >
+8 -7
View File
@@ -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);
+75 -22
View File
@@ -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)
// 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 buf[20];
long_u n;
char_u *langs = get_ui_langs();
// 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)
if (langs != NULL && *langs != NUL)
{
// 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);
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.
+2
View File
@@ -758,6 +758,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
867,
/**/
866,
/**/