@@ -131,6 +131,10 @@ typedef struct
131
131
static HTAB * collation_cache = NULL ;
132
132
133
133
134
+ #if defined(WIN32 ) && defined(LC_MESSAGES )
135
+ static char * IsoLocaleName (const char * ); /* MSVC specific */
136
+ #endif
137
+
134
138
#ifdef USE_ICU
135
139
static char * check_icu_locale (const char * locale );
136
140
#endif
@@ -251,7 +255,7 @@ pg_perm_setlocale(int category, const char *locale, char collprovider)
251
255
envvar = "LC_MESSAGES" ;
252
256
envbuf = lc_messages_envbuf ;
253
257
#ifdef WIN32
254
- result = IsoLocaleName (locale , LC_CTYPE );
258
+ result = IsoLocaleName (locale );
255
259
if (result == NULL )
256
260
result = locale ;
257
261
#endif /* WIN32 */
@@ -972,6 +976,114 @@ cache_locale_time(void)
972
976
}
973
977
974
978
979
+ #if defined(WIN32 ) && defined(LC_MESSAGES )
980
+ /*
981
+ * Convert a Windows setlocale() argument to a Unix-style one.
982
+ *
983
+ * Regardless of platform, we install message catalogs under a Unix-style
984
+ * LL[_CC][.ENCODING][@VARIANT] naming convention. Only LC_MESSAGES settings
985
+ * following that style will elicit localized interface strings.
986
+ *
987
+ * Before Visual Studio 2012 (msvcr110.dll), Windows setlocale() accepted "C"
988
+ * (but not "c") and strings of the form <Language>[_<Country>][.<CodePage>],
989
+ * case-insensitive. setlocale() returns the fully-qualified form; for
990
+ * example, setlocale("thaI") returns "Thai_Thailand.874". Internally,
991
+ * setlocale() and _create_locale() select a "locale identifier"[1] and store
992
+ * it in an undocumented _locale_t field. From that LCID, we can retrieve the
993
+ * ISO 639 language and the ISO 3166 country. Character encoding does not
994
+ * matter, because the server and client encodings govern that.
995
+ *
996
+ * Windows Vista introduced the "locale name" concept[2], closely following
997
+ * RFC 4646. Locale identifiers are now deprecated. Starting with Visual
998
+ * Studio 2012, setlocale() accepts locale names in addition to the strings it
999
+ * accepted historically. It does not standardize them; setlocale("Th-tH")
1000
+ * returns "Th-tH". setlocale(category, "") still returns a traditional
1001
+ * string. Furthermore, msvcr110.dll changed the undocumented _locale_t
1002
+ * content to carry locale names instead of locale identifiers.
1003
+ *
1004
+ * MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol.
1005
+ * IsoLocaleName() always fails in a MinGW-built postgres.exe, so only
1006
+ * Unix-style values of the lc_messages GUC can elicit localized messages. In
1007
+ * particular, every lc_messages setting that initdb can select automatically
1008
+ * will yield only C-locale messages. XXX This could be fixed by running the
1009
+ * fully-qualified locale name through a lookup table.
1010
+ *
1011
+ * This function returns a pointer to a static buffer bearing the converted
1012
+ * name or NULL if conversion fails.
1013
+ *
1014
+ * [1] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373763.aspx
1015
+ * [2] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373814.aspx
1016
+ */
1017
+ static char *
1018
+ IsoLocaleName (const char * winlocname )
1019
+ {
1020
+ #if (_MSC_VER >= 1400 ) /* VC8.0 or later */
1021
+ static char iso_lc_messages [32 ];
1022
+ _locale_t loct = NULL ;
1023
+
1024
+ if (pg_strcasecmp ("c" , winlocname ) == 0 ||
1025
+ pg_strcasecmp ("posix" , winlocname ) == 0 )
1026
+ {
1027
+ strcpy (iso_lc_messages , "C" );
1028
+ return iso_lc_messages ;
1029
+ }
1030
+
1031
+ loct = _create_locale (LC_CTYPE , winlocname );
1032
+ if (loct != NULL )
1033
+ {
1034
+ #if (_MSC_VER >= 1700 ) /* Visual Studio 2012 or later */
1035
+ size_t rc ;
1036
+ char * hyphen ;
1037
+
1038
+ /* Locale names use only ASCII, any conversion locale suffices. */
1039
+ rc = wchar2char (iso_lc_messages , loct -> locinfo -> locale_name [LC_CTYPE ],
1040
+ sizeof (iso_lc_messages ), NULL );
1041
+ _free_locale (loct );
1042
+ if (rc == -1 || rc == sizeof (iso_lc_messages ))
1043
+ return NULL ;
1044
+
1045
+ /*
1046
+ * Since the message catalogs sit on a case-insensitive filesystem, we
1047
+ * need not standardize letter case here. So long as we do not ship
1048
+ * message catalogs for which it would matter, we also need not
1049
+ * translate the script/variant portion, e.g. uz-Cyrl-UZ to
1050
+ * uz_UZ@cyrillic. Simply replace the hyphen with an underscore.
1051
+ *
1052
+ * Note that the locale name can be less-specific than the value we
1053
+ * would derive under earlier Visual Studio releases. For example,
1054
+ * French_France.1252 yields just "fr". This does not affect any of
1055
+ * the country-specific message catalogs available as of this writing
1056
+ * (pt_BR, zh_CN, zh_TW).
1057
+ */
1058
+ hyphen = strchr (iso_lc_messages , '-' );
1059
+ if (hyphen )
1060
+ * hyphen = '_' ;
1061
+ #else
1062
+ char isolang [32 ],
1063
+ isocrty [32 ];
1064
+ LCID lcid ;
1065
+
1066
+ lcid = loct -> locinfo -> lc_handle [LC_CTYPE ];
1067
+ if (lcid == 0 )
1068
+ lcid = MAKELCID (MAKELANGID (LANG_ENGLISH , SUBLANG_ENGLISH_US ), SORT_DEFAULT );
1069
+ _free_locale (loct );
1070
+
1071
+ if (!GetLocaleInfoA (lcid , LOCALE_SISO639LANGNAME , isolang , sizeof (isolang )))
1072
+ return NULL ;
1073
+ if (!GetLocaleInfoA (lcid , LOCALE_SISO3166CTRYNAME , isocrty , sizeof (isocrty )))
1074
+ return NULL ;
1075
+ snprintf (iso_lc_messages , sizeof (iso_lc_messages ) - 1 , "%s_%s" , isolang , isocrty );
1076
+ #endif
1077
+ return iso_lc_messages ;
1078
+ }
1079
+ return NULL ;
1080
+ #else
1081
+ return NULL ; /* Not supported on this version of msvc/mingw */
1082
+ #endif /* _MSC_VER >= 1400 */
1083
+ }
1084
+ #endif /* WIN32 && LC_MESSAGES */
1085
+
1086
+
975
1087
/*
976
1088
* Detect aging strxfrm() implementations that, in a subset of locales, write
977
1089
* past the specified buffer length. Affected users must update OS packages
@@ -1725,32 +1837,36 @@ char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen,
1725
1837
*
1726
1838
* Otherwise return a malloc'd copy of locale if it is not NULL.
1727
1839
*
1728
- * In both cases, return the name of the locale in the Unix style if possible.
1840
+ * In Windows, check that the name is not long (for example,
1841
+ * "English_United States[.encoding]").
1729
1842
*/
1730
1843
static char *
1731
1844
check_icu_locale (const char * locale )
1732
1845
{
1733
1846
char * canonname = NULL ;
1847
+ char * winlocale = NULL ;
1734
1848
char * result ;
1735
1849
1850
+ #ifdef WIN32
1851
+ if (!locale_is_c (locale ))
1852
+ {
1853
+ check_winlocale (locale , NULL , & winlocale );
1854
+ locale = (const char * ) winlocale ;
1855
+ }
1856
+ #else /* not WIN32 */
1736
1857
if (locale && strlen (locale ) == 0 )
1737
1858
{
1738
1859
check_locale (LC_COLLATE , locale , & canonname , COLLPROVIDER_LIBC );
1739
1860
locale = (const char * ) canonname ;
1740
1861
}
1741
-
1742
- #ifdef WIN32
1743
- {
1744
- char * iso_locale = IsoLocaleName (locale , LC_COLLATE );
1745
- if (iso_locale )
1746
- locale = iso_locale ;
1747
- }
1748
- #endif
1862
+ #endif /* not WIN32 */
1749
1863
1750
1864
result = locale ? pstrdup (locale ) : NULL ;
1751
1865
1752
1866
if (canonname )
1753
1867
pfree (canonname );
1868
+ if (winlocale )
1869
+ pfree (winlocale );
1754
1870
1755
1871
return result ;
1756
1872
}
0 commit comments