@@ -224,6 +224,7 @@ pg_perm_setlocale(int category, const char *locale)
224
224
result = IsoLocaleName (locale );
225
225
if (result == NULL )
226
226
result = (char * ) locale ;
227
+ elog (DEBUG3 , "IsoLocaleName() executed; locale: \"%s\"" , result );
227
228
#endif /* WIN32 */
228
229
break ;
229
230
#endif /* LC_MESSAGES */
@@ -957,19 +958,181 @@ cache_locale_time(void)
957
958
* string. Furthermore, msvcr110.dll changed the undocumented _locale_t
958
959
* content to carry locale names instead of locale identifiers.
959
960
*
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.
966
975
*
967
976
* This function returns a pointer to a static buffer bearing the converted
968
977
* name or NULL if conversion fails.
969
978
*
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.
972
1061
*/
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
+
973
1136
static char *
974
1137
IsoLocaleName (const char * winlocname )
975
1138
{
@@ -984,6 +1147,9 @@ IsoLocaleName(const char *winlocname)
984
1147
return iso_lc_messages ;
985
1148
}
986
1149
1150
+ #if (_MSC_VER >= 1900 ) /* Visual Studio 2015 or later */
1151
+ return get_iso_localename (winlocname );
1152
+ #else
987
1153
loct = _create_locale (LC_CTYPE , winlocname );
988
1154
if (loct != NULL )
989
1155
{
@@ -1033,6 +1199,7 @@ IsoLocaleName(const char *winlocname)
1033
1199
return iso_lc_messages ;
1034
1200
}
1035
1201
return NULL ;
1202
+ #endif /* Visual Studio 2015 or later */
1036
1203
#else
1037
1204
return NULL ; /* Not supported on this version of msvc/mingw */
1038
1205
#endif /* _MSC_VER >= 1400 */
0 commit comments