]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - cups/language.c
Merge changes from 1.4svn-r7067.
[thirdparty/cups.git] / cups / language.c
index 3f8f2a8f9ddedaa30d83ff727648606126502fdc..4edaab418048a0cda113a1ca6c806f9e9684934d 100644 (file)
@@ -1,25 +1,16 @@
 /*
- * "$Id: language.c 4985 2006-01-25 21:57:18Z mike $"
+ * "$Id: language.c 6916 2007-09-05 21:14:08Z mike $"
  *
  *   I18N/language support for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 1997-2006 by Easy Software Products.
+ *   Copyright 2007 by Apple Inc.
+ *   Copyright 1997-2007 by Easy Software Products.
  *
  *   These coded instructions, statements, and computer programs are the
- *   property of Easy Software Products and are protected by Federal
- *   copyright law.  Distribution and use rights are outlined in the file
- *   "LICENSE.txt" which should have been included with this file.  If this
- *   file is missing or damaged please contact Easy Software Products
- *   at:
- *
- *       Attn: CUPS Licensing Information
- *       Easy Software Products
- *       44141 Airport View Drive, Suite 204
- *       Hollywood, Maryland 20636 USA
- *
- *       Voice: (301) 373-9600
- *       EMail: cups-info@cups.org
- *         WWW: http://www.cups.org
+ *   property of Apple Inc. and are protected by Federal copyright
+ *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ *   which should have been included with this file.  If this file is
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
  *
  *   This file is subject to the Apple OS-Developed Software exception.
  *
  *   cupsLangEncoding()     - Return the character encoding (us-ascii, etc.)
  *                            for the given language.
  *   cupsLangFlush()        - Flush all language data out of the cache.
- *   _cupsLangFlush()       - Flush all language data out of the cache.
  *   cupsLangFree()         - Free language data.
  *   cupsLangGet()          - Get a language.
  *   _cupsLangString()      - Get a message string.
  *   _cupsMessageFree()     - Free a messages array.
  *   _cupsMessageLoad()     - Load a .po file into a messages array.
  *   _cupsMessageLookup()   - Lookup a message string.
- *   _cupsRestoreLocale()   - Restore the original locale...
- *   _cupsSaveLocale()      - Set the locale and save a copy of the old locale...
  *   appleLangDefault()     - Get the default locale string.
  *   cups_cache_lookup()    - Lookup a language in the cache...
  *   cups_message_compare() - Compare two messages.
@@ -53,6 +41,7 @@
 #include "globals.h"
 #include "debug.h"
 #include <stdlib.h>
+#include <errno.h>
 #ifdef HAVE_LANGINFO_H
 #  include <langinfo.h>
 #endif /* HAVE_LANGINFO_H */
 #endif /* HAVE_COREFOUNDATION_H */
 
 
+/*
+ * Local globals...
+ */
+
+#ifdef HAVE_PTHREAD_H
+static pthread_mutex_t lang_mutex = PTHREAD_MUTEX_INITIALIZER;
+                                       /* Mutex to control access to cache */
+#endif /* HAVE_PTHREAD_H */
+static cups_lang_t     *lang_cache = NULL;
+                                       /* Language string cache */
+
+
 /*
  * Local functions...
  */
@@ -100,7 +101,7 @@ static const char * const lang_encodings[] =
                          "windows-1256",       "windows-1257",
                          "windows-1258",       "koi8-r",
                          "koi8-u",             "iso-8859-11",
-                         "iso-8859-16",        "unknown",
+                         "iso-8859-16",        "mac-roman",
                          "unknown",            "unknown",
                          "unknown",            "unknown",
                          "unknown",            "unknown",
@@ -204,17 +205,6 @@ cupsLangEncoding(cups_lang_t *lang)        /* I - Language data */
 
 void
 cupsLangFlush(void)
-{
-  _cupsLangFlush(_cupsGlobals());
-}
-
-
-/*
- * '_cupsLangFlush()' - Flush all language data out of the cache.
- */
-
-void
-_cupsLangFlush(_cups_globals_t *cg)    /* I - Global data */
 {
   cups_lang_t  *lang,                  /* Current language */
                *next;                  /* Next language */
@@ -224,7 +214,11 @@ _cupsLangFlush(_cups_globals_t *cg)        /* I - Global data */
   * Free all languages in the cache...
   */
 
-  for (lang = cg->lang_cache; lang != NULL; lang = next)
+#ifdef HAVE_PTHREAD_H
+  pthread_mutex_lock(&lang_mutex);
+#endif /* HAVE_PTHREAD_H */
+
+  for (lang = lang_cache; lang != NULL; lang = next)
   {
    /*
     * Free all messages...
@@ -240,7 +234,11 @@ _cupsLangFlush(_cups_globals_t *cg)        /* I - Global data */
     free(lang);
   }
 
-  cg->lang_cache = NULL;
+  lang_cache = NULL;
+
+#ifdef HAVE_PTHREAD_H
+  pthread_mutex_unlock(&lang_mutex);
+#endif /* HAVE_PTHREAD_H */
 }
 
 
@@ -253,8 +251,16 @@ _cupsLangFlush(_cups_globals_t *cg)        /* I - Global data */
 void
 cupsLangFree(cups_lang_t *lang)                /* I - Language to free */
 {
+#ifdef HAVE_PTHREAD_H
+  pthread_mutex_lock(&lang_mutex);
+#endif /* HAVE_PTHREAD_H */
+
   if (lang != NULL && lang->used > 0)
     lang->used --;
+
+#ifdef HAVE_PTHREAD_H
+  pthread_mutex_unlock(&lang_mutex);
+#endif /* HAVE_PTHREAD_H */
 }
 
 
@@ -266,19 +272,18 @@ cups_lang_t *                             /* O - Language data */
 cupsLangGet(const char *language)      /* I - Language or locale */
 {
   int                  i;              /* Looping var */
-  char                 locale[255],    /* Copy of locale name */
-                       langname[16],   /* Requested language name */
+#ifndef __APPLE__
+  char                 locale[255];    /* Copy of locale name */
+#endif /* !__APPLE__ */
+  char                 langname[16],   /* Requested language name */
                        country[16],    /* Country code */
                        charset[16],    /* Character set */
-#ifdef CODESET
                        *csptr,         /* Pointer to CODESET string */
-#endif /* CODESET */
                        *ptr,           /* Pointer into language/charset */
                        real[48],       /* Real language name */
                        filename[1024]; /* Filename for language locale file */
   cups_encoding_t      encoding;       /* Encoding to use */
   cups_lang_t          *lang;          /* Current language... */
-  char                 *oldlocale;     /* Old locale name */
   _cups_globals_t      *cg = _cupsGlobals();
                                        /* Pointer to library globals */
   static const char * const locale_encodings[] =
@@ -290,7 +295,7 @@ cupsLangGet(const char *language)   /* I - Language or locale */
                  "CP1250",     "CP1251",       "CP1252",       "CP1253",
                  "CP1254",     "CP1255",       "CP1256",       "CP1257",
                  "CP1258",     "KOI8R",        "KOI8U",        "ISO885911",
-                 "ISO885916",  "",             "",             "",
+                 "ISO885916",  "MACROMAN",     "",             "",
 
                  "",           "",             "",             "",
                  "",           "",             "",             "",
@@ -326,19 +331,38 @@ cupsLangGet(const char *language) /* I - Language or locale */
   DEBUG_printf(("cupsLangGet(language=\"%s\")\n", language ? language : "(null)"));
 
 #ifdef __APPLE__
+ /*
+  * Set the character set to UTF-8...
+  */
+
+  strcpy(charset, "UTF8");
+
  /*
   * Apple's setlocale doesn't give us the user's localization 
   * preference so we have to look it up this way...
   */
 
-  if (language == NULL)
+  if (!language && (language = getenv("LANG")) == NULL)
     language = appleLangDefault();
+
 #else
-  if (language == NULL)
+ /*
+  * Set the charset to "unknown"...
+  */
+
+  charset[0] = '\0';
+
+ /*
+  * Use setlocale() to determine the currently set locale, and then
+  * fallback to environment variables to avoid setting the locale,
+  * since setlocale() is not thread-safe!
+  */
+
+  if (!language)
   {
    /*
     * First see if the locale has been set; if it is still "C" or
-    * "POSIX", set the locale to the default...
+    * "POSIX", use the environment to get the default...
     */
 
 #  ifdef LC_MESSAGES
@@ -351,22 +375,52 @@ cupsLangGet(const char *language) /* I - Language or locale */
                   ptr ? ptr : "(null)"));
 
     if (!ptr || !strcmp(ptr, "C") || !strcmp(ptr, "POSIX"))
-#  ifdef LC_MESSAGES
     {
-      ptr = setlocale(LC_MESSAGES, "");
-      setlocale(LC_CTYPE, "");
+     /*
+      * Get the character set from the LC_CTYPE locale setting...
+      */
+
+      if ((ptr = getenv("LC_CTYPE")) == NULL)
+        if ((ptr = getenv("LC_ALL")) == NULL)
+         if ((ptr = getenv("LANG")) == NULL)
+           ptr = "en_US";
+
+      if ((csptr = strchr(ptr, '.')) != NULL)
+      {
+       /*
+        * Extract the character set from the environment...
+       */
+
+       for (ptr = charset, csptr ++; *csptr; csptr ++)
+         if (ptr < (charset + sizeof(charset) - 1) && isalnum(*csptr & 255))
+           *ptr++ = *csptr;
+
+        *ptr = '\0';
+      }
+
+     /*
+      * Get the locale for messages from the LC_MESSAGES locale setting...
+      */
+
+      if ((ptr = getenv("LC_MESSAGES")) == NULL)
+        if ((ptr = getenv("LC_ALL")) == NULL)
+         if ((ptr = getenv("LANG")) == NULL)
+           ptr = "en_US";
     }
-#  else
-      ptr = setlocale(LC_ALL, "");
-#  endif /* LC_MESSAGES */
 
     if (ptr)
     {
       strlcpy(locale, ptr, sizeof(locale));
       language = locale;
 
-      DEBUG_printf(("cupsLangGet: new language value is \"%s\"\n",
-                    language ? language : "(null)"));
+     /*
+      * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
+      */
+
+      if (!strncmp(locale, "nb", 2))
+        locale[1] = 'o';
+
+      DEBUG_printf(("cupsLangGet: new language value is \"%s\"\n", language));
     }
   }
 #endif /* __APPLE__ */
@@ -379,27 +433,19 @@ cupsLangGet(const char *language) /* I - Language or locale */
   if (!language)
   {
    /*
-    * Switch to the value of the "LANG" environment variable, and if
-    * that is NULL as well, use "C".
+    * Switch to the POSIX ("C") locale...
     */
 
-    if ((language = getenv("LANG")) == NULL)
-      language = "C";
+    language = "C";
   }
 
- /*
-  * Set the charset to "unknown"...
-  */
-
-  charset[0] = '\0';
-
 #ifdef CODESET
  /*
   * On systems that support the nl_langinfo(CODESET) call, use
   * this value as the character set...
   */
 
-  if ((csptr = nl_langinfo(CODESET)) != NULL)
+  if (!charset[0] && (csptr = nl_langinfo(CODESET)) != NULL)
   {
    /*
     * Copy all of the letters and numbers in the CODESET string...
@@ -417,20 +463,11 @@ cupsLangGet(const char *language) /* I - Language or locale */
 #endif /* CODESET */
 
  /*
-  * Set the locale back to POSIX while we do string ops, since
-  * apparently some buggy C libraries break ctype() for non-I18N
-  * chars...
+  * If we don't have a character set by now, default to UTF-8...
   */
 
-#if defined(__APPLE__)
-  /* The ctype bug isn't in Apple's libc */
-  (void)locale;                        /* anti-compiler-warning-code */
-  (void)oldlocale;             /* anti-compiler-warning-code */
-#elif !defined(LC_CTYPE)
-  oldlocale = _cupsSaveLocale(LC_ALL, "C");
-#else
-  oldlocale = _cupsSaveLocale(LC_CTYPE, "C");
-#endif /* __APPLE__ */
+  if (!charset[0])
+    strcpy(charset, "UTF8");
 
  /*
   * Parse the language string passed in to a locale string. "C" is the
@@ -500,18 +537,6 @@ cupsLangGet(const char *language)  /* I - Language or locale */
     }
   }
 
- /*
-  * Restore the locale...
-  */
-
-#if defined(__APPLE__)
-  /* The ctype bug isn't in Apple's libc */
-#elif !defined(LC_CTYPE)
-  _cupsRestoreLocale(LC_ALL, oldlocale);
-#else
-  _cupsRestoreLocale(LC_CTYPE, oldlocale);
-#endif /* __APPLE__ */
-
   DEBUG_printf(("cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"\n",
                 langname, country, charset));
 
@@ -523,12 +548,31 @@ cupsLangGet(const char *language) /* I - Language or locale */
 
   if (charset[0])
   {
-    for (i = 0; i < (int)(sizeof(locale_encodings) / sizeof(locale_encodings[0])); i ++)
+    for (i = 0;
+         i < (int)(sizeof(locale_encodings) / sizeof(locale_encodings[0]));
+        i ++)
       if (!strcasecmp(charset, locale_encodings[i]))
       {
        encoding = (cups_encoding_t)i;
        break;
       }
+
+    if (encoding == CUPS_AUTO_ENCODING)
+    {
+     /*
+      * Map alternate names for various character sets...
+      */
+
+      if (!strcasecmp(charset, "iso-2022-jp") ||
+          !strcasecmp(charset, "sjis"))
+       encoding = CUPS_WINDOWS_932;
+      else if (!strcasecmp(charset, "iso-2022-cn"))
+       encoding = CUPS_WINDOWS_936;
+      else if (!strcasecmp(charset, "iso-2022-kr"))
+       encoding = CUPS_WINDOWS_949;
+      else if (!strcasecmp(charset, "big5"))
+       encoding = CUPS_WINDOWS_950;
+    }
   }
 
   DEBUG_printf(("cupsLangGet: encoding=%d(%s)\n", encoding,
@@ -539,13 +583,31 @@ cupsLangGet(const char *language) /* I - Language or locale */
   * See if we already have this language/country loaded...
   */
 
-  snprintf(real, sizeof(real), "%s_%s", langname, country);
+  if (country[0])
+  {
+    snprintf(real, sizeof(real), "%s_%s", langname, country);
+
+    snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
+             real, real);
+  }
+  else
+  {
+    strcpy(real, langname);
+    filename[0] = '\0';                        /* anti-compiler-warning-code */
+  }
+
+#ifdef HAVE_PTHREAD_H
+  pthread_mutex_lock(&lang_mutex);
+#endif /* HAVE_PTHREAD_H */
 
   if ((lang = cups_cache_lookup(real, encoding)) != NULL)
-    return (lang);
+  {
+#ifdef HAVE_PTHREAD_H
+    pthread_mutex_unlock(&lang_mutex);
+#endif /* HAVE_PTHREAD_H */
 
-  snprintf(filename, sizeof(filename), "%s/%s/cups_%s", cg->localedir,
-           real, real);
+    return (lang);
+  }
 
   if (!country[0] || access(filename, 0))
   {
@@ -553,10 +615,7 @@ cupsLangGet(const char *language)  /* I - Language or locale */
     * Country localization not available, look for generic localization...
     */
 
-    if ((lang = cups_cache_lookup(langname, encoding)) != NULL)
-      return (lang);
-
-    snprintf(filename, sizeof(filename), "%s/%s/cups_%s", cg->localedir,
+    snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
              langname, langname);
 
     if (access(filename, 0))
@@ -565,11 +624,10 @@ cupsLangGet(const char *language) /* I - Language or locale */
       * No generic localization, so use POSIX...
       */
 
-      strcpy(real, "C");
-      snprintf(filename, sizeof(filename), "%s/C/cups_C", cg->localedir);
+      DEBUG_printf(("access(\"%s\", 0): %s\n", filename, strerror(errno)));
+
+      snprintf(filename, sizeof(filename), "%s/C/cups_C.po", cg->localedir);
     }
-    else
-      strcpy(real, langname);
   }
 
  /*
@@ -577,7 +635,7 @@ cupsLangGet(const char *language)   /* I - Language or locale */
   * record...
   */
 
-  for (lang = cg->lang_cache; lang != NULL; lang = lang->next)
+  for (lang = lang_cache; lang != NULL; lang = lang->next)
     if (lang->used == 0)
       break;
 
@@ -588,17 +646,25 @@ cupsLangGet(const char *language) /* I - Language or locale */
     */
 
     if ((lang = calloc(sizeof(cups_lang_t), 1)) == NULL)
+    {
+#ifdef HAVE_PTHREAD_H
+      pthread_mutex_unlock(&lang_mutex);
+#endif /* HAVE_PTHREAD_H */
+
       return (NULL);
+    }
 
-    lang->next     = cg->lang_cache;
-    cg->lang_cache = lang;
+    lang->next lang_cache;
+    lang_cache = lang;
   }
+  else
+  {
+   /*
+    * Free all old strings as needed...
+    */
 
- /*
-  * Free all old strings as needed...
-  */
-
-  _cupsMessageFree(lang->strings);
+    _cupsMessageFree(lang->strings);
+  }
 
  /*
   * Then assign the language and encoding fields...
@@ -622,6 +688,10 @@ cupsLangGet(const char *language)  /* I - Language or locale */
   * Return...
   */
 
+#ifdef HAVE_PTHREAD_H
+  pthread_mutex_unlock(&lang_mutex);
+#endif /* HAVE_PTHREAD_H */
+
   return (lang);
 }
 
@@ -644,7 +714,21 @@ _cupsLangString(cups_lang_t *lang, /* I - Language */
   if (!lang || !message)
     return (message);
 
+#ifdef HAVE_PTHREAD_H
+  {
+    const char *s;                     /* Localized message */
+
+    pthread_mutex_lock(&lang_mutex);
+
+    s = _cupsMessageLookup(lang->strings, message);
+
+    pthread_mutex_unlock(&lang_mutex);
+
+    return (s);
+  }
+#else
   return (_cupsMessageLookup(lang->strings, message));
+#endif /* HAVE_PTHREAD_H */
 }
 
 
@@ -701,6 +785,8 @@ _cupsMessageLoad(const char *filename)      /* I - Message catalog to load */
   int                  length;         /* Length of combined strings */
 
 
+  DEBUG_printf(("_cupsMessageLoad(filename=\"%s\")\n", filename));
+
  /*
   * Create an array to hold the messages...
   */
@@ -723,10 +809,12 @@ _cupsMessageLoad(const char *filename)    /* I - Message catalog to load */
   *     msgid "some text"
   *     msgstr "localized text"
   *
-  * The localized text can span multiple lines using the form:
+  * The ID and localized text can span multiple lines using the form:
   *
-  *     msgid "some long text"
-  *     msgstr "localized text spanning "
+  *     msgid ""
+  *     "some long text"
+  *     msgstr ""
+  *     "localized text spanning "
   *     "multiple lines"
   */
 
@@ -771,6 +859,17 @@ _cupsMessageLoad(const char *filename)     /* I - Message catalog to load */
 
     if (!strncmp(s, "msgid", 5))
     {
+     /*
+      * Add previous message as needed...
+      */
+
+      if (m)
+        cupsArrayAdd(a, m);
+
+     /*
+      * Create a new message with the given msgid string...
+      */
+
       if ((m = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
       {
         cupsFileClose(fp);
@@ -778,44 +877,64 @@ _cupsMessageLoad(const char *filename)    /* I - Message catalog to load */
       }
 
       m->id = strdup(ptr);
-      cupsArrayAdd(a, m);
     }
-    else if ((s[0] == '\"' || !strncmp(s, "msgstr", 6)) && m)
+    else if (s[0] == '\"' && m)
     {
-      if (m->str)
-      {
-       /*
-       * Append the string...
-       */
+     /*
+      * Append to current string...
+      */
 
-       length = strlen(m->str);
+      length = (int)strlen(m->str ? m->str : m->id);
 
-       if ((temp = realloc(m->str, length + strlen(ptr) + 1)) == NULL)
-       {
-         cupsFileClose(fp);
-         return (a);
-       }
-       else
-         m->str = temp;
+      if ((temp = realloc(m->str ? m->str : m->id,
+                          length + strlen(ptr) + 1)) == NULL)
+      {
+       cupsFileClose(fp);
+       return (a);
+      }
 
+      if (m->str)
+      {
        /*
-        * Copy the new portion at the end - safe because the buffer is
-       * allocated to the correct size...
+        * Copy the new portion to the end of the msgstr string - safe
+       * to use strcpy because the buffer is allocated to the correct
+       * size...
        */
 
+        m->str = temp;
+
        strcpy(m->str + length, ptr);
       }
       else
       {
        /*
-       * Set the string...
+        * Copy the new portion to the end of the msgid string - safe
+       * to use strcpy because the buffer is allocated to the correct
+       * size...
        */
 
-       m->str = strdup(ptr);
+        m->id = temp;
+
+       strcpy(m->id + length, ptr);
       }
     }
+    else if (!strncmp(s, "msgstr", 6) && m)
+    {
+     /*
+      * Set the string...
+      */
+
+      m->str = strdup(ptr);
+    }
   }
 
+ /*
+  * Add the last message string to the array as needed...
+  */
+
+  if (m)
+    cupsArrayAdd(a, m);
+
  /*
   * Close the message catalog file and return the new array...
   */
@@ -853,66 +972,6 @@ _cupsMessageLookup(cups_array_t *a,        /* I - Message array */
 }
 
 
-/*
- * '_cupsRestoreLocale()' - Restore the original locale...
- */
-
-void
-_cupsRestoreLocale(int  category,      /* I - Category */
-                   char *oldlocale)    /* I - Old locale or NULL */
-{
-  DEBUG_printf(("_cupsRestoreLocale(category=%d, oldlocale=\"%s\")\n",
-                category, oldlocale));
-
-  if (oldlocale)
-  {
-   /*
-    * Reset the locale and free the locale string...
-    */
-
-    setlocale(category, oldlocale);
-    free(oldlocale);
-  }
-}
-
-
-/*
- * '_cupsSaveLocale()' - Set the locale and save a copy of the old locale...
- */
-
-char *                                 /* O - Old locale or NULL */
-_cupsSaveLocale(int        category,   /* I - Category */
-                const char *locale)    /* I - New locale or NULL */
-{
-  char *oldlocale;                     /* Old locale */
-
-
-  DEBUG_printf(("_cupsSaveLocale(category=%d, locale=\"%s\")\n",
-                category, locale));
-
- /*
-  * Get the old locale and copy it...
-  */
-
-  if ((oldlocale = setlocale(category, NULL)) != NULL)
-    oldlocale = strdup(oldlocale);
-
-  DEBUG_printf(("    oldlocale=\"%s\"\n", oldlocale ? oldlocale : "(null)"));
-
- /*
-  * Set the new locale...
-  */
-
-  setlocale(category, locale);
-
- /*
-  * Return a copy of the old locale...
-  */
-
-  return (oldlocale);
-}
-
-
 #ifdef __APPLE__
 /*
  * Code & data to translate OSX's language names to their ISO 639-1 locale.
@@ -922,6 +981,22 @@ _cupsSaveLocale(int        category,       /* I - Category */
  */
 
 #  ifdef HAVE_CF_LOCALE_ID
+
+typedef struct
+{
+  const char * const name;             /* Language name */
+  const char * const locale;           /* Locale name */
+} _apple_name_locale_t;
+
+static const _apple_name_locale_t apple_name_locale[] =
+{
+  { "en"       , "en_US" },
+  { "nb"       , "no"    },
+  { "zh-Hans"  , "zh_CN" },
+  { "zh-Hant"  , "zh_TW" }
+};
+
+
 /*
  * 'appleLangDefault()' - Get the default locale string.
  */
@@ -929,10 +1004,14 @@ _cupsSaveLocale(int        category,     /* I - Category */
 static const char *                    /* O - Locale string */
 appleLangDefault(void)
 {
+  int                  i;              /* Looping var */
+  CFBundleRef          bundle;         /* Main bundle (if any) */
+  CFArrayRef           bundleList;     /* List of localizations in bundle */
   CFPropertyListRef    localizationList;
                                        /* List of localization data */
   CFStringRef          languageName;   /* Current name */
   CFStringRef          localeName;     /* Canonical from of name */
+  char                 *lang;          /* LANG environment variable */
   _cups_globals_t      *cg = _cupsGlobals();
                                        /* Pointer to library globals */
 
@@ -943,35 +1022,75 @@ appleLangDefault(void)
 
   if (!cg->language[0])
   {
-    localizationList =
-        CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
-                                  kCFPreferencesCurrentApplication);
+    if ((lang = getenv("LANG")))
+    {
+      strlcpy(cg->language, lang, sizeof(cg->language));
+      return (cg->language);
+    }
+    else if ((bundle = CFBundleGetMainBundle()) != NULL &&
+             (bundleList = CFBundleCopyBundleLocalizations(bundle)) != NULL)
+    {
+      localizationList =
+         CFBundleCopyPreferredLocalizationsFromArray(bundleList);
+
+      CFRelease(bundleList);
+    }
+    else
+      localizationList =
+         CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
+                                   kCFPreferencesCurrentApplication);
 
-    if (localizationList != NULL)
+    if (localizationList)
     {
       if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
          CFArrayGetCount(localizationList) > 0)
       {
-        languageName = CFArrayGetValueAtIndex(localizationList, 0);
+       languageName = CFArrayGetValueAtIndex(localizationList, 0);
 
-        if (languageName != NULL &&
-            CFGetTypeID(languageName) == CFStringGetTypeID())
-        {
+       if (languageName &&
+           CFGetTypeID(languageName) == CFStringGetTypeID())
+       {
          localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(
-                          kCFAllocatorDefault, languageName);
+                          kCFAllocatorDefault, languageName);
 
-         if (localeName != NULL)
+         if (localeName)
          {
            CFStringGetCString(localeName, cg->language, sizeof(cg->language),
                               kCFStringEncodingASCII);
            CFRelease(localeName);
 
-           if (!strcmp(cg->language, "en"))
-             strlcpy(cg->language, "en_US.UTF-8", sizeof(cg->language));
-           else if (strchr(cg->language, '.') == NULL)
+           DEBUG_printf(("appleLangDefault: cg->language=\"%s\"\n",
+                         cg->language));
+
+          /*
+           * Map new language identifiers to locales...
+           */
+
+           for (i = 0;
+                i < sizeof(apple_name_locale) / sizeof(apple_name_locale[0]);
+                i++)
+           {
+             if (!strcmp(cg->language, apple_name_locale[i].name))
+             {
+               DEBUG_printf(("appleLangDefault: mapping \"%s\" to \"%s\"...\n",
+                             cg->language, apple_name_locale[i].locale));
+               strlcpy(cg->language, apple_name_locale[i].locale, 
+                       sizeof(cg->language));
+               break;
+             }
+           }
+
+          /*
+           * Convert language subtag into region subtag...
+           */
+
+           if (cg->language[2] == '-')
+             cg->language[2] = '_';
+
+           if (!strchr(cg->language, '.'))
              strlcat(cg->language, ".UTF-8", sizeof(cg->language));
          }
-        }
+       }
       }
 
       CFRelease(localizationList);
@@ -1082,59 +1201,66 @@ appleLangDefault(void)
   char                 buff[256];      /* Temporary buffer */
   _cups_globals_t      *cg = _cupsGlobals();
                                        /* Pointer to library globals */
+  char                 *lang;          /* LANG environment variable */
 
 
  /*
   * Only do the lookup and translation the first time.
   */
 
-  if (cg->language == NULL)
+  if (!cg->language[0])
   {
-    localizationList =
-        CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
-                                  kCFPreferencesCurrentApplication);
-
-    if (localizationList != NULL)
+    if ((lang = getenv("LANG")))
+      strlcpy(cg->language, lang, sizeof(cg->language));
+    else
     {
-      if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
-         CFArrayGetCount(localizationList) > 0)
-      {
-       localizationName = CFArrayGetValueAtIndex(localizationList, 0);
+      localizationList =
+          CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
+                                    kCFPreferencesCurrentApplication);
 
-       if (localizationName != NULL &&
-            CFGetTypeID(localizationName) == CFStringGetTypeID())
+      if (localizationList != NULL)
+      {
+       if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
+           CFArrayGetCount(localizationList) > 0)
        {
-         CFIndex length = CFStringGetLength(localizationName);
+         localizationName = CFArrayGetValueAtIndex(localizationList, 0);
 
-         if (length <= sizeof(buff) &&
-             CFStringGetCString(localizationName, buff, sizeof(buff),
-                                kCFStringEncodingASCII))
+         if (localizationName != NULL &&
+              CFGetTypeID(localizationName) == CFStringGetTypeID())
          {
-           buff[sizeof(buff) - 1] = '\0';
+           CFIndex length = CFStringGetLength(localizationName);
 
-           for (i = 0;
-                i < sizeof(apple_name_locale) / sizeof(apple_name_locale[0]);
-                i++)
+           if (length <= sizeof(buff) &&
+               CFStringGetCString(localizationName, buff, sizeof(buff),
+                                  kCFStringEncodingASCII))
            {
-             if (!strcasecmp(buff, apple_name_locale[i].name))
+             buff[sizeof(buff) - 1] = '\0';
+
+             for (i = 0;
+                  i < sizeof(apple_name_locale) / sizeof(apple_name_locale[0]);
+                  i++)
              {
-               cg->language = apple_name_locale[i].locale;
-               break;
+               if (!strcasecmp(buff, apple_name_locale[i].name))
+               {
+                 strlcpy(cg->language, apple_name_locale[i].locale, 
+                         sizeof(cg->language));
+                 break;
+               }
              }
            }
          }
        }
-      }
 
-      CFRelease(localizationList);
+       CFRelease(localizationList);
+      }
     }
   
    /*
     * If we didn't find the language, default to en_US...
     */
 
-    if (cg->language == NULL)
-      cg->language = apple_name_locale[0].locale;
+    if (!cg->language[0])
+      strlcpy(cg->language, apple_name_locale[0].locale, sizeof(cg->language));
   }
 
  /*
@@ -1167,7 +1293,7 @@ cups_cache_lookup(const char      *name,/* I - Name of locale */
   * Loop through the cache and return a match if found...
   */
 
-  for (lang = _cupsGlobals()->lang_cache; lang != NULL; lang = lang->next)
+  for (lang = lang_cache; lang != NULL; lang = lang->next)
   {
     DEBUG_printf(("cups_cache_lookup: lang=%p, language=\"%s\", encoding=%d(%s)\n",
                   lang, lang->language, lang->encoding,
@@ -1225,6 +1351,8 @@ cups_unquote(char       *d,               /* O - Unquoted string */
          *d = *d * 8 + *s - '0';
          s ++;
        }
+
+       d ++;
       }
       else
       {
@@ -1249,5 +1377,5 @@ cups_unquote(char       *d,               /* O - Unquoted string */
 
 
 /*
- * End of "$Id: language.c 4985 2006-01-25 21:57:18Z mike $".
+ * End of "$Id: language.c 6916 2007-09-05 21:14:08Z mike $".
  */