@@ -224,6 +224,7 @@ pg_perm_setlocale(int category, const char *locale)
224224result = IsoLocaleName (locale );
225225if (result == NULL )
226226result = (char * )locale ;
227+ elog (DEBUG3 ,"IsoLocaleName() executed; locale: \"%s\"" ,result );
227228#endif /* WIN32 */
228229break ;
229230#endif /* LC_MESSAGES */
@@ -957,19 +958,181 @@ cache_locale_time(void)
957958 * string. Furthermore, msvcr110.dll changed the undocumented _locale_t
958959 * content to carry locale names instead of locale identifiers.
959960 *
960- * MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol.
961- * IsoLocaleName() always fails in a MinGW-built postgres.exe, so only
962- * Unix-style values of the lc_messages GUC can elicit localized messages. In
963- * particular, every lc_messages setting that initdb can select automatically
964- * will yield only C-locale messages. XXX This could be fixed by running the
965- * fully-qualified locale name through a lookup table.
961+ * Visual Studio 2015 should still be able to do the same as Visual Studio
962+ * 2012, but the declaration of locale_name is missing in _locale_t, causing
963+ * this code compilation to fail, hence this falls back instead on to
964+ * enumerating all system locales by using EnumSystemLocalesEx to find the
965+ * required locale name. If the input argument is in Unix-style then we can
966+ * get ISO Locale name directly by using GetLocaleInfoEx() with LCType as
967+ * LOCALE_SNAME.
968+ *
969+ * MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol in
970+ * releases before Windows 8. IsoLocaleName() always fails in a MinGW-built
971+ * postgres.exe, so only Unix-style values of the lc_messages GUC can elicit
972+ * localized messages. In particular, every lc_messages setting that initdb
973+ * can select automatically will yield only C-locale messages. XXX This could
974+ * be fixed by running the fully-qualified locale name through a lookup table.
966975 *
967976 * This function returns a pointer to a static buffer bearing the converted
968977 * name or NULL if conversion fails.
969978 *
970- * [1] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373763.aspx
971- * [2] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373814.aspx
979+ * [1] https://docs.microsoft.com/en-us/windows/win32/intl/locale-identifiers
980+ * [2] https://docs.microsoft.com/en-us/windows/win32/intl/locale-names
981+ */
982+
983+ #if _MSC_VER >=1900
984+ /*
985+ * Callback function for EnumSystemLocalesEx() in get_iso_localename().
986+ *
987+ * This function enumerates all system locales, searching for one that matches
988+ * an input with the format: <Language>[_<Country>], e.g.
989+ * English[_United States]
990+ *
991+ * The input is a three wchar_t array as an LPARAM. The first element is the
992+ * locale_name we want to match, the second element is an allocated buffer
993+ * where the Unix-style locale is copied if a match is found, and the third
994+ * element is the search status, 1 if a match was found, 0 otherwise.
995+ */
996+ static BOOL CALLBACK
997+ search_locale_enum (LPWSTR pStr ,DWORD dwFlags ,LPARAM lparam )
998+ {
999+ wchar_t test_locale [LOCALE_NAME_MAX_LENGTH ];
1000+ wchar_t * * argv ;
1001+
1002+ (void ) (dwFlags );
1003+
1004+ argv = (wchar_t * * )lparam ;
1005+ * argv [2 ]= (wchar_t )0 ;
1006+
1007+ memset (test_locale ,0 ,sizeof (test_locale ));
1008+
1009+ /* Get the name of the <Language> in English */
1010+ if (GetLocaleInfoEx (pStr ,LOCALE_SENGLISHLANGUAGENAME ,
1011+ test_locale ,LOCALE_NAME_MAX_LENGTH ))
1012+ {
1013+ /*
1014+ * If the enumerated locale does not have a hyphen ("en") OR the
1015+ * lc_message input does not have an underscore ("English"), we only
1016+ * need to compare the <Language> tags.
1017+ */
1018+ if (wcsrchr (pStr ,'-' )== NULL || wcsrchr (argv [0 ],'_' )== NULL )
1019+ {
1020+ if (_wcsicmp (argv [0 ],test_locale )== 0 )
1021+ {
1022+ wcscpy (argv [1 ],pStr );
1023+ * argv [2 ]= (wchar_t )1 ;
1024+ return FALSE;
1025+ }
1026+ }
1027+
1028+ /*
1029+ * We have to compare a full <Language>_<Country> tag, so we append
1030+ * the underscore and name of the country/region in English, e.g.
1031+ * "English_United States".
1032+ */
1033+ else
1034+ {
1035+ size_t len ;
1036+
1037+ wcscat (test_locale ,L"_" );
1038+ len = wcslen (test_locale );
1039+ if (GetLocaleInfoEx (pStr ,LOCALE_SENGLISHCOUNTRYNAME ,
1040+ test_locale + len ,
1041+ LOCALE_NAME_MAX_LENGTH - len ))
1042+ {
1043+ if (_wcsicmp (argv [0 ],test_locale )== 0 )
1044+ {
1045+ wcscpy (argv [1 ],pStr );
1046+ * argv [2 ]= (wchar_t )1 ;
1047+ return FALSE;
1048+ }
1049+ }
1050+ }
1051+ }
1052+
1053+ return TRUE;
1054+ }
1055+
1056+ /*
1057+ * This function converts a Windows locale name to an ISO formatted version
1058+ * for Visual Studio 2015 or greater.
1059+ *
1060+ * Returns NULL, if no valid conversion was found.
9721061 */
1062+ static char *
1063+ get_iso_localename (const char * winlocname )
1064+ {
1065+ wchar_t wc_locale_name [LOCALE_NAME_MAX_LENGTH ];
1066+ wchar_t buffer [LOCALE_NAME_MAX_LENGTH ];
1067+ static char iso_lc_messages [LOCALE_NAME_MAX_LENGTH ];
1068+ char * period ;
1069+ int len ;
1070+ int ret_val ;
1071+
1072+ /*
1073+ * Valid locales have the following syntax:
1074+ * <Language>[_<Country>[.<CodePage>]]
1075+ *
1076+ * GetLocaleInfoEx can only take locale name without code-page and for the
1077+ * purpose of this API the code-page doesn't matter.
1078+ */
1079+ period = strchr (winlocname ,'.' );
1080+ if (period != NULL )
1081+ len = period - winlocname ;
1082+ else
1083+ len = pg_mbstrlen (winlocname );
1084+
1085+ memset (wc_locale_name ,0 ,sizeof (wc_locale_name ));
1086+ memset (buffer ,0 ,sizeof (buffer ));
1087+ MultiByteToWideChar (CP_ACP ,0 ,winlocname ,len ,wc_locale_name ,
1088+ LOCALE_NAME_MAX_LENGTH );
1089+
1090+ /*
1091+ * If the lc_messages is already an Unix-style string, we have a direct
1092+ * match with LOCALE_SNAME, e.g. en-US, en_US.
1093+ */
1094+ ret_val = GetLocaleInfoEx (wc_locale_name ,LOCALE_SNAME , (LPWSTR )& buffer ,
1095+ LOCALE_NAME_MAX_LENGTH );
1096+ if (!ret_val )
1097+ {
1098+ /*
1099+ * Search for a locale in the system that matches language and country
1100+ * name.
1101+ */
1102+ wchar_t * argv [3 ];
1103+
1104+ argv [0 ]= wc_locale_name ;
1105+ argv [1 ]= buffer ;
1106+ argv [2 ]= (wchar_t * )& ret_val ;
1107+ EnumSystemLocalesEx (search_locale_enum ,LOCALE_WINDOWS , (LPARAM )argv ,
1108+ NULL );
1109+ }
1110+
1111+ if (ret_val )
1112+ {
1113+ size_t rc ;
1114+ char * hyphen ;
1115+
1116+ /* Locale names use only ASCII, any conversion locale suffices. */
1117+ rc = wchar2char (iso_lc_messages ,buffer ,sizeof (iso_lc_messages ),NULL );
1118+ if (rc == -1 || rc == sizeof (iso_lc_messages ))
1119+ return NULL ;
1120+
1121+ /*
1122+ * Simply replace the hyphen with an underscore. See comments in
1123+ * IsoLocaleName.
1124+ */
1125+ hyphen = strchr (iso_lc_messages ,'-' );
1126+ if (hyphen )
1127+ * hyphen = '_' ;
1128+
1129+ return iso_lc_messages ;
1130+ }
1131+
1132+ return NULL ;
1133+ }
1134+ #endif /* _MSC_VER >= 1900 */
1135+
9731136static char *
9741137IsoLocaleName (const char * winlocname )
9751138{
@@ -984,6 +1147,9 @@ IsoLocaleName(const char *winlocname)
9841147return iso_lc_messages ;
9851148}
9861149
1150+ #if (_MSC_VER >=1900 )/* Visual Studio 2015 or later */
1151+ return get_iso_localename (winlocname );
1152+ #else
9871153loct = _create_locale (LC_CTYPE ,winlocname );
9881154if (loct != NULL )
9891155{
@@ -1033,6 +1199,7 @@ IsoLocaleName(const char *winlocname)
10331199return iso_lc_messages ;
10341200}
10351201return NULL ;
1202+ #endif /* Visual Studio 2015 or later */
10361203#else
10371204return NULL ;/* Not supported on this version of msvc/mingw */
10381205#endif /* _MSC_VER >= 1400 */