setlocale: Improve locale handling on macOS 10.12 or newer.
authorBruno Haible <bruno@clisp.org>
Sun, 16 Sep 2018 17:12:44 +0000 (19:12 +0200)
committerBruno Haible <bruno@clisp.org>
Sun, 16 Sep 2018 17:12:44 +0000 (19:12 +0200)
* lib/setlocale.c: Include header files for CoreFoundation. Declare
gl_locale_name_canonicalize.
(libintl_setlocale): Try harder to set a locale for categories LC_CTYPE
and LC_MESSAGES.
* m4/setlocale.m4 (gl_PREREQ_SETLOCALE): Add comment.

ChangeLog
lib/setlocale.c
m4/setlocale.m4

index 5fb968259537068e39cb7793d891c9725cb2d341..51608264a5f19042c01d8236195d1192b626e3d3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2018-09-16  Bruno Haible  <bruno@clisp.org>
+
+       setlocale: Improve locale handling on macOS 10.12 or newer.
+       * lib/setlocale.c: Include header files for CoreFoundation. Declare
+       gl_locale_name_canonicalize.
+       (libintl_setlocale): Try harder to set a locale for categories LC_CTYPE
+       and LC_MESSAGES.
+       * m4/setlocale.m4 (gl_PREREQ_SETLOCALE): Add comment.
+
 2018-09-16  Bruno Haible  <bruno@clisp.org>
 
        Update list of locale names with scripts on macOS.
index 77bdabab46c086aecc98f08c9dba16e8706e116b..63f870f1df1398af4a947323671e0bb76d94b7d7 100644 (file)
 
 #include "localename.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>
+extern void gl_locale_name_canonicalize (char *name);
+#endif
+
 #if 1
 
 # undef setlocale
@@ -892,14 +904,81 @@ rpl_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
index 802ee01fa9fbc956ba9352786bf7653a00937de5..e0fb74a92ddf5ac57409c6b8f72e399fa4579261 100644 (file)
@@ -1,4 +1,4 @@
-# setlocale.m4 serial 4
+# setlocale.m4 serial 5
 dnl Copyright (C) 2011-2018 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -25,5 +25,8 @@ AC_DEFUN([gl_FUNC_SETLOCALE],
 # Prerequisites of lib/setlocale.c.
 AC_DEFUN([gl_PREREQ_SETLOCALE],
 [
+  dnl No need to check for CFLocaleCopyPreferredLanguages and
+  dnl CFPreferencesCopyAppValue because lib/setlocale.c is not used on Mac OS X.
+  dnl (The Mac OS X specific code is only used in libintl.)
   :
 ])