@@ -234,6 +234,7 @@ pg_perm_setlocale(int category, const char *locale)
234
234
result = IsoLocaleName (locale );
235
235
if (result == NULL )
236
236
result = (char * ) locale ;
237
+ elog (DEBUG3 , "IsoLocaleName() executed; locale: \"%s\"" , result );
237
238
#endif /* WIN32 */
238
239
break ;
239
240
#endif /* LC_MESSAGES */
@@ -971,65 +972,234 @@ cache_locale_time(void)
971
972
* string. Furthermore, msvcr110.dll changed the undocumented _locale_t
972
973
* content to carry locale names instead of locale identifiers.
973
974
*
974
- * MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol.
975
- * IsoLocaleName() always fails in a MinGW-built postgres.exe, so only
976
- * Unix-style values of the lc_messages GUC can elicit localized messages. In
977
- * particular, every lc_messages setting that initdb can select automatically
978
- * will yield only C-locale messages. XXX This could be fixed by running the
979
- * fully-qualified locale name through a lookup table.
975
+ * Visual Studio 2015 should still be able to do the same as Visual Studio
976
+ * 2012, but the declaration of locale_name is missing in _locale_t, causing
977
+ * this code compilation to fail, hence this falls back instead on to
978
+ * enumerating all system locales by using EnumSystemLocalesEx to find the
979
+ * required locale name. If the input argument is in Unix-style then we can
980
+ * get ISO Locale name directly by using GetLocaleInfoEx() with LCType as
981
+ * LOCALE_SNAME.
982
+ *
983
+ * MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol in
984
+ * releases before Windows 8. IsoLocaleName() always fails in a MinGW-built
985
+ * postgres.exe, so only Unix-style values of the lc_messages GUC can elicit
986
+ * localized messages. In particular, every lc_messages setting that initdb
987
+ * can select automatically will yield only C-locale messages. XXX This could
988
+ * be fixed by running the fully-qualified locale name through a lookup table.
980
989
*
981
990
* This function returns a pointer to a static buffer bearing the converted
982
991
* name or NULL if conversion fails.
983
992
*
984
- * [1] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373763.aspx
985
- * [2] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373814.aspx
993
+ * [1] https://docs.microsoft.com/en-us/windows/win32/intl/locale-identifiers
994
+ * [2] https://docs.microsoft.com/en-us/windows/win32/intl/locale-names
995
+ */
996
+
997
+ #if _MSC_VER >= 1900
998
+ /*
999
+ * Callback function for EnumSystemLocalesEx() in get_iso_localename().
1000
+ *
1001
+ * This function enumerates all system locales, searching for one that matches
1002
+ * an input with the format: <Language>[_<Country>], e.g.
1003
+ * English[_United States]
1004
+ *
1005
+ * The input is a three wchar_t array as an LPARAM. The first element is the
1006
+ * locale_name we want to match, the second element is an allocated buffer
1007
+ * where the Unix-style locale is copied if a match is found, and the third
1008
+ * element is the search status, 1 if a match was found, 0 otherwise.
1009
+ */
1010
+ static BOOL CALLBACK
1011
+ search_locale_enum (LPWSTR pStr , DWORD dwFlags , LPARAM lparam )
1012
+ {
1013
+ wchar_t test_locale [LOCALE_NAME_MAX_LENGTH ];
1014
+ wchar_t * * argv ;
1015
+
1016
+ (void ) (dwFlags );
1017
+
1018
+ argv = (wchar_t * * ) lparam ;
1019
+ * argv [2 ] = (wchar_t ) 0 ;
1020
+
1021
+ memset (test_locale , 0 , sizeof (test_locale ));
1022
+
1023
+ /* Get the name of the <Language> in English */
1024
+ if (GetLocaleInfoEx (pStr , LOCALE_SENGLISHLANGUAGENAME ,
1025
+ test_locale , LOCALE_NAME_MAX_LENGTH ))
1026
+ {
1027
+ /*
1028
+ * If the enumerated locale does not have a hyphen ("en") OR the
1029
+ * lc_message input does not have an underscore ("English"), we only
1030
+ * need to compare the <Language> tags.
1031
+ */
1032
+ if (wcsrchr (pStr , '-' ) == NULL || wcsrchr (argv [0 ], '_' ) == NULL )
1033
+ {
1034
+ if (_wcsicmp (argv [0 ], test_locale ) == 0 )
1035
+ {
1036
+ wcscpy (argv [1 ], pStr );
1037
+ * argv [2 ] = (wchar_t ) 1 ;
1038
+ return FALSE;
1039
+ }
1040
+ }
1041
+
1042
+ /*
1043
+ * We have to compare a full <Language>_<Country> tag, so we append
1044
+ * the underscore and name of the country/region in English, e.g.
1045
+ * "English_United States".
1046
+ */
1047
+ else
1048
+ {
1049
+ size_t len ;
1050
+
1051
+ wcscat (test_locale , L"_" );
1052
+ len = wcslen (test_locale );
1053
+ if (GetLocaleInfoEx (pStr , LOCALE_SENGLISHCOUNTRYNAME ,
1054
+ test_locale + len ,
1055
+ LOCALE_NAME_MAX_LENGTH - len ))
1056
+ {
1057
+ if (_wcsicmp (argv [0 ], test_locale ) == 0 )
1058
+ {
1059
+ wcscpy (argv [1 ], pStr );
1060
+ * argv [2 ] = (wchar_t ) 1 ;
1061
+ return FALSE;
1062
+ }
1063
+ }
1064
+ }
1065
+ }
1066
+
1067
+ return TRUE;
1068
+ }
1069
+
1070
+ /*
1071
+ * This function converts a Windows locale name to an ISO formatted version
1072
+ * for Visual Studio 2015 or greater.
1073
+ *
1074
+ * Returns NULL, if no valid conversion was found.
986
1075
*/
987
1076
static char *
988
- IsoLocaleName (const char * winlocname )
1077
+ get_iso_localename (const char * winlocname )
989
1078
{
990
- #ifdef _MSC_VER
991
- static char iso_lc_messages [32 ];
992
- _locale_t loct = NULL ;
1079
+ wchar_t wc_locale_name [LOCALE_NAME_MAX_LENGTH ];
1080
+ wchar_t buffer [LOCALE_NAME_MAX_LENGTH ];
1081
+ static char iso_lc_messages [LOCALE_NAME_MAX_LENGTH ];
1082
+ char * period ;
1083
+ int len ;
1084
+ int ret_val ;
993
1085
994
- if (pg_strcasecmp ("c" , winlocname ) == 0 ||
995
- pg_strcasecmp ("posix" , winlocname ) == 0 )
1086
+ /*
1087
+ * Valid locales have the following syntax:
1088
+ * <Language>[_<Country>[.<CodePage>]]
1089
+ *
1090
+ * GetLocaleInfoEx can only take locale name without code-page and for the
1091
+ * purpose of this API the code-page doesn't matter.
1092
+ */
1093
+ period = strchr (winlocname , '.' );
1094
+ if (period != NULL )
1095
+ len = period - winlocname ;
1096
+ else
1097
+ len = pg_mbstrlen (winlocname );
1098
+
1099
+ memset (wc_locale_name , 0 , sizeof (wc_locale_name ));
1100
+ memset (buffer , 0 , sizeof (buffer ));
1101
+ MultiByteToWideChar (CP_ACP , 0 , winlocname , len , wc_locale_name ,
1102
+ LOCALE_NAME_MAX_LENGTH );
1103
+
1104
+ /*
1105
+ * If the lc_messages is already an Unix-style string, we have a direct
1106
+ * match with LOCALE_SNAME, e.g. en-US, en_US.
1107
+ */
1108
+ ret_val = GetLocaleInfoEx (wc_locale_name , LOCALE_SNAME , (LPWSTR ) & buffer ,
1109
+ LOCALE_NAME_MAX_LENGTH );
1110
+ if (!ret_val )
996
1111
{
997
- strcpy (iso_lc_messages , "C" );
998
- return iso_lc_messages ;
1112
+ /*
1113
+ * Search for a locale in the system that matches language and country
1114
+ * name.
1115
+ */
1116
+ wchar_t * argv [3 ];
1117
+
1118
+ argv [0 ] = wc_locale_name ;
1119
+ argv [1 ] = buffer ;
1120
+ argv [2 ] = (wchar_t * ) & ret_val ;
1121
+ EnumSystemLocalesEx (search_locale_enum , LOCALE_WINDOWS , (LPARAM ) argv ,
1122
+ NULL );
999
1123
}
1000
1124
1001
- loct = _create_locale (LC_CTYPE , winlocname );
1002
- if (loct != NULL )
1125
+ if (ret_val )
1003
1126
{
1004
1127
size_t rc ;
1005
1128
char * hyphen ;
1006
1129
1007
1130
/* Locale names use only ASCII, any conversion locale suffices. */
1008
- rc = wchar2char (iso_lc_messages , loct -> locinfo -> locale_name [LC_CTYPE ],
1009
- sizeof (iso_lc_messages ), NULL );
1010
- _free_locale (loct );
1131
+ rc = wchar2char (iso_lc_messages , buffer , sizeof (iso_lc_messages ), NULL );
1011
1132
if (rc == -1 || rc == sizeof (iso_lc_messages ))
1012
1133
return NULL ;
1013
1134
1014
1135
/*
1015
- * Since the message catalogs sit on a case-insensitive filesystem, we
1016
- * need not standardize letter case here. So long as we do not ship
1017
- * message catalogs for which it would matter, we also need not
1018
- * translate the script/variant portion, e.g. uz-Cyrl-UZ to
1019
- * uz_UZ@cyrillic. Simply replace the hyphen with an underscore.
1020
- *
1021
- * Note that the locale name can be less-specific than the value we
1022
- * would derive under earlier Visual Studio releases. For example,
1023
- * French_France.1252 yields just "fr". This does not affect any of
1024
- * the country-specific message catalogs available as of this writing
1025
- * (pt_BR, zh_CN, zh_TW).
1136
+ * Simply replace the hyphen with an underscore. See comments in
1137
+ * IsoLocaleName.
1026
1138
*/
1027
1139
hyphen = strchr (iso_lc_messages , '-' );
1028
1140
if (hyphen )
1029
1141
* hyphen = '_' ;
1030
1142
return iso_lc_messages ;
1031
1143
}
1032
- #endif /* _MSC_VER */
1144
+
1145
+ return NULL ;
1146
+ }
1147
+ #endif /* _MSC_VER >= 1900 */
1148
+
1149
+ static char *
1150
+ IsoLocaleName (const char * winlocname )
1151
+ {
1152
+ #if defined(_MSC_VER )
1153
+ static char iso_lc_messages [LOCALE_NAME_MAX_LENGTH ];
1154
+
1155
+ if (pg_strcasecmp ("c" , winlocname ) == 0 ||
1156
+ pg_strcasecmp ("posix" , winlocname ) == 0 )
1157
+ {
1158
+ strcpy (iso_lc_messages , "C" );
1159
+ return iso_lc_messages ;
1160
+ }
1161
+ else
1162
+ {
1163
+ #if (_MSC_VER >= 1900 ) /* Visual Studio 2015 or later */
1164
+ return get_iso_localename (winlocname );
1165
+ #else
1166
+ _locale_t loct ;
1167
+
1168
+ loct = _create_locale (LC_CTYPE , winlocname );
1169
+ if (loct != NULL )
1170
+ {
1171
+ size_t rc ;
1172
+ char * hyphen ;
1173
+
1174
+ /* Locale names use only ASCII, any conversion locale suffices. */
1175
+ rc = wchar2char (iso_lc_messages , loct -> locinfo -> locale_name [LC_CTYPE ],
1176
+ sizeof (iso_lc_messages ), NULL );
1177
+ _free_locale (loct );
1178
+ if (rc == -1 || rc == sizeof (iso_lc_messages ))
1179
+ return NULL ;
1180
+
1181
+ /*
1182
+ * Since the message catalogs sit on a case-insensitive
1183
+ * filesystem, we need not standardize letter case here. So long
1184
+ * as we do not ship message catalogs for which it would matter,
1185
+ * we also need not translate the script/variant portion, e.g.
1186
+ * uz-Cyrl-UZ to uz_UZ@cyrillic. Simply replace the hyphen with
1187
+ * an underscore.
1188
+ *
1189
+ * Note that the locale name can be less-specific than the value
1190
+ * we would derive under earlier Visual Studio releases. For
1191
+ * example, French_France.1252 yields just "fr". This does not
1192
+ * affect any of the country-specific message catalogs available
1193
+ * as of this writing (pt_BR, zh_CN, zh_TW).
1194
+ */
1195
+ hyphen = strchr (iso_lc_messages , '-' );
1196
+ if (hyphen )
1197
+ * hyphen = '_' ;
1198
+ return iso_lc_messages ;
1199
+ }
1200
+ #endif /* Visual Studio 2015 or later */
1201
+ }
1202
+ #endif /* defined(_MSC_VER) */
1033
1203
return NULL ; /* Not supported on this version of msvc/mingw */
1034
1204
}
1035
1205
#endif /* WIN32 && LC_MESSAGES */
0 commit comments