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