@@ -715,12 +715,41 @@ cache_locale_time(void)
715715
716716#if defined(WIN32 ) && defined(LC_MESSAGES )
717717/*
718- * Convert Windows locale name to the ISO formatted one
719- * if possible.
718+ * Convert a Windows setlocale() argument to a Unix-style one.
720719 *
721- * This function returns NULL if conversion is impossible,
722- * otherwise returns the pointer to a static area which
723- * contains the iso formatted locale name.
720+ * Regardless of platform, we install message catalogs under a Unix-style
721+ * LL[_CC][.ENCODING][@VARIANT] naming convention. Only LC_MESSAGES settings
722+ * following that style will elicit localized interface strings.
723+ *
724+ * Before Visual Studio 2012 (msvcr110.dll), Windows setlocale() accepted "C"
725+ * (but not "c") and strings of the form <Language>[_<Country>][.<CodePage>],
726+ * case-insensitive. setlocale() returns the fully-qualified form; for
727+ * example, setlocale("thaI") returns "Thai_Thailand.874". Internally,
728+ * setlocale() and _create_locale() select a "locale identifier"[1] and store
729+ * it in an undocumented _locale_t field. From that LCID, we can retrieve the
730+ * ISO 639 language and the ISO 3166 country. Character encoding does not
731+ * matter, because the server and client encodings govern that.
732+ *
733+ * Windows Vista introduced the "locale name" concept[2], closely following
734+ * RFC 4646. Locale identifiers are now deprecated. Starting with Visual
735+ * Studio 2012, setlocale() accepts locale names in addition to the strings it
736+ * accepted historically. It does not standardize them; setlocale("Th-tH")
737+ * returns "Th-tH". setlocale(category, "") still returns a traditional
738+ * string. Furthermore, msvcr110.dll changed the undocumented _locale_t
739+ * content to carry locale names instead of locale identifiers.
740+ *
741+ * MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol.
742+ * IsoLocaleName() always fails in a MinGW-built postgres.exe, so only
743+ * Unix-style values of the lc_messages GUC can elicit localized messages. In
744+ * particular, every lc_messages setting that initdb can select automatically
745+ * will yield only C-locale messages. XXX This could be fixed by running the
746+ * fully-qualified locale name through a lookup table.
747+ *
748+ * This function returns a pointer to a static buffer bearing the converted
749+ * name or NULL if conversion fails.
750+ *
751+ * [1] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373763.aspx
752+ * [2] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373814.aspx
724753 */
725754static char *
726755IsoLocaleName (const char * winlocname )
@@ -739,6 +768,34 @@ IsoLocaleName(const char *winlocname)
739768 loct = _create_locale (LC_CTYPE , winlocname );
740769 if (loct != NULL )
741770 {
771+ #if (_MSC_VER >= 1700 ) /* Visual Studio 2012 or later */
772+ size_t rc ;
773+ char * hyphen ;
774+
775+ /* Locale names use only ASCII, any conversion locale suffices. */
776+ rc = wchar2char (iso_lc_messages , loct -> locinfo -> locale_name [LC_CTYPE ],
777+ sizeof (iso_lc_messages ), NULL );
778+ _free_locale (loct );
779+ if (rc == -1 || rc == sizeof (iso_lc_messages ))
780+ return NULL ;
781+
782+ /*
783+ * Since the message catalogs sit on a case-insensitive filesystem, we
784+ * need not standardize letter case here. So long as we do not ship
785+ * message catalogs for which it would matter, we also need not
786+ * translate the script/variant portion, e.g. uz-Cyrl-UZ to
787+ * uz_UZ@cyrillic. Simply replace the hyphen with an underscore.
788+ *
789+ * Note that the locale name can be less-specific than the value we
790+ * would derive under earlier Visual Studio releases. For example,
791+ * French_France.1252 yields just "fr". This does not affect any of
792+ * the country-specific message catalogs available as of this writing
793+ * (pt_BR, zh_CN, zh_TW).
794+ */
795+ hyphen = strchr (iso_lc_messages , '-' );
796+ if (hyphen )
797+ * hyphen = '_' ;
798+ #else
742799 char isolang [32 ],
743800 isocrty [32 ];
744801 LCID lcid ;
@@ -753,6 +810,7 @@ IsoLocaleName(const char *winlocname)
753810 if (!GetLocaleInfoA (lcid , LOCALE_SISO3166CTRYNAME , isocrty , sizeof (isocrty )))
754811 return NULL ;
755812 snprintf (iso_lc_messages , sizeof (iso_lc_messages ) - 1 , "%s_%s" , isolang , isocrty );
813+ #endif
756814 return iso_lc_messages ;
757815 }
758816 return NULL ;
0 commit comments