Commit: patch 9.2.0867: MS-Windows: messages are not in the display language

Christian Brabandt <[email protected]> Tue, 28 Jul 2026 20:15:04 +0200
Newsgroups gmane.editors.vim.devel
Message-ID <[email protected]>
patch 9.2.0867: MS-Windows: messages are not in the display language

Commit: https://github.com/vim/vim/commit/930dccfed0617979b836dfefbbe57d5c26a6679d
Author: Hirohito Higashi <[email protected]>
Date:   Mon Jul 27 22:04:06 2026 +0000

    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) <[email protected]>
    Signed-off-by: Hirohito Higashi <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/runtime/doc/mlang.txt b/runtime/doc/mlang.txt
index 6f0bb3fe6..0c1747eed 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 eb1845fce..0d217fd43 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 d4f63203d..f511b8ea5 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'
+	    {
+		bool	english = s[0] == L'e' && s[1] == L'n'
+					&& (s[2] == L'
+
+		if (d > langs)
+		    *d++ = ':';
+		// Locale names are ASCII, "en-US" becomes "en_US".
+		for ( ; *s != L'
+		    *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 48389e931..af42c7cbd 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,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups "vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1womKW-00199A-BC%40256bit.org.