changeset 39846:dbb326309f2a

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.
author Bruno Haible <bruno@clisp.org>
date Sun, 16 Sep 2018 19:12:44 +0200
parents 6b89ce1f6b37
children 7245e73971d4
files ChangeLog lib/setlocale.c m4/setlocale.m4
diffstat 3 files changed, 100 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Sep 16 17:14:12 2018 +0200
+++ b/ChangeLog	Sun Sep 16 19:12:44 2018 +0200
@@ -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.
--- a/lib/setlocale.c	Sun Sep 16 17:14:12 2018 +0200
+++ b/lib/setlocale.c	Sun Sep 16 19:12:44 2018 +0200
@@ -35,6 +35,18 @@
 
 #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 @@
                  )
                 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
--- a/m4/setlocale.m4	Sun Sep 16 17:14:12 2018 +0200
+++ b/m4/setlocale.m4	Sun Sep 16 19:12:44 2018 +0200
@@ -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 @@
 # 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.)
   :
 ])