From: Bruno Haible Date: Sun, 16 Sep 2018 17:09:41 +0000 (+0200) Subject: libintl: Improve locale handling on macOS 10.12 or newer. X-Git-Tag: v0.20~387 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=17b1b16277d81af3afd5c51c53eda385e33f3178;p=thirdparty%2Fgettext.git libintl: Improve locale handling on macOS 10.12 or newer. * gettext-runtime/intl/langprefs.c (_nl_language_preferences_default): Make the logic also work in locales such as "zh-Hans-DE". * gettext-runtime/intl/setlocale.c: Include header files for CoreFoundation. (libintl_setlocale): Try harder to set a locale for categories LC_CTYPE and LC_MESSAGES. --- diff --git a/gettext-runtime/intl/langprefs.c b/gettext-runtime/intl/langprefs.c index 93c0aad5e..207962bd5 100644 --- a/gettext-runtime/intl/langprefs.c +++ b/gettext-runtime/intl/langprefs.c @@ -260,6 +260,7 @@ _nl_language_preferences_default (void) int n = CFArrayGetCount (prefArray); char buf[256]; + char buf2[256]; size_t size = 0; int i; @@ -272,19 +273,30 @@ _nl_language_preferences_default (void) buf, sizeof (buf), kCFStringEncodingASCII)) { + strcpy (buf2, buf); _nl_locale_name_canonicalize (buf); size += strlen (buf) + 1; /* Mac OS X 10.12 or newer returns an array of elements of - the form "ll-CC" where ll is a language code and CC is a - country code. _nl_locale_name_canonicalize converts this - to "ll_CC". Sometimes ll and CC are unrelated, i.e. - there is no translation for "ll_CC" but one for "ll". + the form "ll-CC" or "ll-Scrp-CC" where ll is a language + code, CC is a country code, and Scrp (optional) is a + script code. + _nl_locale_name_canonicalize converts this to "ll_CC" or + "ll_Scrp_CC". + Sometimes ll and CC are unrelated, i.e. there is no + translation for "ll_CC" but one for "ll". + Similarly, in the case with a script, sometimes there is + no translation for "ll_Scrp_CC" but one for "ll_Scrp" + (after proper canonicalization). Therefore, in the result, we return "ll_CC" followed - by "ll". */ + by "ll", or similarly for the case with a script. */ { - char *underscore = strchr (buf, '_'); - if (underscore != NULL) - size += (underscore - buf) + 1; + char *last_minus = strrchr (buf2, '-'); + if (last_minus != NULL) + { + *last_minus = '\0'; + _nl_locale_name_canonicalize (buf2); + size += strlen (buf2) + 1; + } } /* Most GNU programs use msgids in English and don't ship an en.mo message catalog. Therefore when we see "en" or @@ -316,16 +328,19 @@ _nl_language_preferences_default (void) buf, sizeof (buf), kCFStringEncodingASCII)) { + strcpy (buf2, buf); _nl_locale_name_canonicalize (buf); strcpy (p, buf); p += strlen (buf); *p++ = ':'; { - char *underscore = strchr (buf, '_'); - if (underscore != NULL) + char *last_minus = strrchr (buf2, '-'); + if (last_minus != NULL) { - memcpy (p, buf, underscore - buf); - p += underscore - buf; + *last_minus = '\0'; + _nl_locale_name_canonicalize (buf2); + strcpy (p, buf2); + p += strlen (buf2); *p++ = ':'; } } diff --git a/gettext-runtime/intl/setlocale.c b/gettext-runtime/intl/setlocale.c index 3b770c02d..a2fb849bd 100644 --- a/gettext-runtime/intl/setlocale.c +++ b/gettext-runtime/intl/setlocale.c @@ -43,6 +43,17 @@ #include "gettextP.h" +#if HAVE_CFLOCALECOPYPREFERREDLANGUAGES || HAVE_CFPREFERENCESCOPYAPPVALUE +# if HAVE_CFLOCALECOPYPREFERREDLANGUAGES +# include +# elif HAVE_CFPREFERENCESCOPYAPPVALUE +# include +# endif +# include +# include +# include +#endif + #if (defined __APPLE__ && defined __MACH__) || defined _WIN32 || defined __CYGWIN__ # undef setlocale @@ -902,14 +913,81 @@ libintl_setlocale (int category, const char *locale) ) if (setlocale_single (cat, name) == NULL) # if defined __APPLE__ && defined __MACH__ - /* On Mac OS X 10.13, some locales can be set through - System Preferences > Language & Region, that are not - supported by libc. The system's setlocale() falls - back to "C" for these locale categories. Let's do the - same, but print a warning, to limit user expectations. */ - fprintf (stderr, - "Warning: Failed to set locale category %s to %s.\n", - category_to_name (cat), name); + { + /* On Mac OS X 10.13, some locales can be set through + System Preferences > Language & Region, that are not + supported by libc. The system's setlocale() falls + back to "C" for these locale categories. We can possibly + do better. If we can't, print a warning, to limit user + expectations. */ + int warn = 1; + + if (cat == LC_CTYPE) + warn = (setlocale_single (cat, "UTF-8") == NULL); +# if HAVE_CFLOCALECOPYPREFERREDLANGUAGES || HAVE_CFPREFERENCESCOPYAPPVALUE /* MacOS X 10.4 or newer */ + else if (cat == LC_MESSAGES) + { + /* Take the primary language preference. */ +# if HAVE_CFLOCALECOPYPREFERREDLANGUAGES /* MacOS X 10.5 or newer */ + CFArrayRef prefArray = CFLocaleCopyPreferredLanguages (); +# elif HAVE_CFPREFERENCESCOPYAPPVALUE /* MacOS X 10.4 or newer */ + CFTypeRef preferences = + CFPreferencesCopyAppValue (CFSTR ("AppleLanguages"), + kCFPreferencesCurrentApplication); + if (preferences != NULL + && CFGetTypeID (preferences) == CFArrayGetTypeID ()) + { + CFArrayRef prefArray = (CFArrayRef)preferences; +# endif + int n = CFArrayGetCount (prefArray); + if (n > 0) + { + char buf[256]; + CFTypeRef element = CFArrayGetValueAtIndex (prefArray, 0); + if (element != NULL + && CFGetTypeID (element) == CFStringGetTypeID () + && CFStringGetCString ((CFStringRef)element, + buf, sizeof (buf), + kCFStringEncodingASCII)) + { + /* Remove the country. + E.g. "zh-Hans-DE" -> "zh-Hans". */ + char *last_minus = strrchr (buf, '-'); + if (last_minus != NULL) + *last_minus = '\0'; + + /* Convert to Unix locale name. + E.g. "zh-Hans" -> "zh_CN". */ + gl_locale_name_canonicalize (buf); + + /* Try setlocale with this value. */ + warn = (setlocale_single (cat, buf) == NULL); + } + } +# if HAVE_CFLOCALECOPYPREFERREDLANGUAGES /* MacOS X 10.5 or newer */ + CFRelease (prefArray); +# elif HAVE_CFPREFERENCESCOPYAPPVALUE /* MacOS X 10.4 or newer */ + } +# endif + } +# endif + /* No fallback possible for LC_NUMERIC. The application + should use the locale properties + kCFLocaleDecimalSeparator, kCFLocaleGroupingSeparator. + No fallback possible for LC_TIME. The application should + use the locale property kCFLocaleCalendarIdentifier. + No fallback possible for LC_COLLATE. The application + should use the locale properties + kCFLocaleCollationIdentifier, kCFLocaleCollatorIdentifier. + No fallback possible for LC_MONETARY. The application + should use the locale properties + kCFLocaleCurrencySymbol, kCFLocaleCurrencyCode. */ + + if (warn) + fprintf (stderr, + "Warning: Failed to set locale category %s to %s.\n", + category_to_name (cat), name); + } # else goto fail; # endif