aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/pg_locale.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/pg_locale.c')
-rw-r--r--src/backend/utils/adt/pg_locale.c68
1 files changed, 63 insertions, 5 deletions
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 495fe4db6ae..604c11ee83f 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -714,12 +714,41 @@ cache_locale_time(void)
#if defined(WIN32) && defined(LC_MESSAGES)
/*
- * Convert Windows locale name to the ISO formatted one
- * if possible.
+ * Convert a Windows setlocale() argument to a Unix-style one.
*
- * This function returns NULL if conversion is impossible,
- * otherwise returns the pointer to a static area which
- * contains the iso formatted locale name.
+ * Regardless of platform, we install message catalogs under a Unix-style
+ * LL[_CC][.ENCODING][@VARIANT] naming convention. Only LC_MESSAGES settings
+ * following that style will elicit localized interface strings.
+ *
+ * Before Visual Studio 2012 (msvcr110.dll), Windows setlocale() accepted "C"
+ * (but not "c") and strings of the form <Language>[_<Country>][.<CodePage>],
+ * case-insensitive. setlocale() returns the fully-qualified form; for
+ * example, setlocale("thaI") returns "Thai_Thailand.874". Internally,
+ * setlocale() and _create_locale() select a "locale identifier"[1] and store
+ * it in an undocumented _locale_t field. From that LCID, we can retrieve the
+ * ISO 639 language and the ISO 3166 country. Character encoding does not
+ * matter, because the server and client encodings govern that.
+ *
+ * Windows Vista introduced the "locale name" concept[2], closely following
+ * RFC 4646. Locale identifiers are now deprecated. Starting with Visual
+ * Studio 2012, setlocale() accepts locale names in addition to the strings it
+ * accepted historically. It does not standardize them; setlocale("Th-tH")
+ * returns "Th-tH". setlocale(category, "") still returns a traditional
+ * string. Furthermore, msvcr110.dll changed the undocumented _locale_t
+ * content to carry locale names instead of locale identifiers.
+ *
+ * MinGW headers declare _create_locale(), but msvcrt.dll lacks that symbol.
+ * IsoLocaleName() always fails in a MinGW-built postgres.exe, so only
+ * Unix-style values of the lc_messages GUC can elicit localized messages. In
+ * particular, every lc_messages setting that initdb can select automatically
+ * will yield only C-locale messages. XXX This could be fixed by running the
+ * fully-qualified locale name through a lookup table.
+ *
+ * This function returns a pointer to a static buffer bearing the converted
+ * name or NULL if conversion fails.
+ *
+ * [1] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373763.aspx
+ * [2] http://msdn.microsoft.com/en-us/library/windows/desktop/dd373814.aspx
*/
static char *
IsoLocaleName(const char *winlocname)
@@ -738,6 +767,34 @@ IsoLocaleName(const char *winlocname)
loct = _create_locale(LC_CTYPE, winlocname);
if (loct != NULL)
{
+#if (_MSC_VER >= 1700) /* Visual Studio 2012 or later */
+ size_t rc;
+ char *hyphen;
+
+ /* Locale names use only ASCII, any conversion locale suffices. */
+ rc = wchar2char(iso_lc_messages, loct->locinfo->locale_name[LC_CTYPE],
+ sizeof(iso_lc_messages), NULL);
+ _free_locale(loct);
+ if (rc == -1 || rc == sizeof(iso_lc_messages))
+ return NULL;
+
+ /*
+ * Since the message catalogs sit on a case-insensitive filesystem, we
+ * need not standardize letter case here. So long as we do not ship
+ * message catalogs for which it would matter, we also need not
+ * translate the script/variant portion, e.g. uz-Cyrl-UZ to
+ * uz_UZ@cyrillic. Simply replace the hyphen with an underscore.
+ *
+ * Note that the locale name can be less-specific than the value we
+ * would derive under earlier Visual Studio releases. For example,
+ * French_France.1252 yields just "fr". This does not affect any of
+ * the country-specific message catalogs available as of this writing
+ * (pt_BR, zh_CN, zh_TW).
+ */
+ hyphen = strchr(iso_lc_messages, '-');
+ if (hyphen)
+ *hyphen = '_';
+#else
char isolang[32],
isocrty[32];
LCID lcid;
@@ -752,6 +809,7 @@ IsoLocaleName(const char *winlocname)
if (!GetLocaleInfoA(lcid, LOCALE_SISO3166CTRYNAME, isocrty, sizeof(isocrty)))
return NULL;
snprintf(iso_lc_messages, sizeof(iso_lc_messages) - 1, "%s_%s", isolang, isocrty);
+#endif
return iso_lc_messages;
}
return NULL;