]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
libintl: Improve locale handling on macOS 10.12 or newer.
authorBruno Haible <bruno@clisp.org>
Sun, 16 Sep 2018 17:09:41 +0000 (19:09 +0200)
committerBruno Haible <bruno@clisp.org>
Sun, 16 Sep 2018 17:09:41 +0000 (19:09 +0200)
* 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.

gettext-runtime/intl/langprefs.c
gettext-runtime/intl/setlocale.c

index 93c0aad5e976a00edbbf1105dd3f9ca297f8b6ad..207962bd5addd840985b240ac559ada0c7eac450 100644 (file)
@@ -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++ = ':';
                                 }
                             }
index 3b770c02d805dc1adcc51e07dbb0bd9549c05b8d..a2fb849bd926f484621083b076295a0adc115877 100644 (file)
 
 #include "gettextP.h"
 
+#if HAVE_CFLOCALECOPYPREFERREDLANGUAGES || HAVE_CFPREFERENCESCOPYAPPVALUE
+# if HAVE_CFLOCALECOPYPREFERREDLANGUAGES
+#  include <CoreFoundation/CFLocale.h>
+# elif HAVE_CFPREFERENCESCOPYAPPVALUE
+#  include <CoreFoundation/CFPreferences.h>
+# endif
+# include <CoreFoundation/CFPropertyList.h>
+# include <CoreFoundation/CFArray.h>
+# include <CoreFoundation/CFString.h>
+#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