/*
- * "$Id: language.c 5373 2006-04-06 20:03:32Z mike $"
+ * "$Id$"
*
- * I18N/language support for the Common UNIX Printing System (CUPS).
+ * I18N/language support for CUPS.
*
- * Copyright 1997-2006 by Easy Software Products.
+ * Copyright 2007-2012 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.
*
* Contents:
*
- * _cupsEncodingName() - Return the character encoding name string
- * for the given encoding enumeration.
- * cupsLangDefault() - Return the default language.
+ * _cupsAppleLanguage() - Get the Apple language identifier associated with
+ * a locale ID.
+ * _cupsEncodingName() - Return the character encoding name string for the
+ * given encoding enumeration.
+ * cupsLangDefault() - Return the default language.
* cupsLangEncoding() - Return the character encoding (us-ascii, etc.)
- * for the given language.
- * cupsLangFlush() - Flush all language data out of the cache.
- * cupsLangFree() - Free language data.
- * cupsLangGet() - Get a language.
- * _cupsLangString() - Get a message string.
+ * for the given language.
+ * 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.
+ * _cupsMessageNew() - Make a new message catalog array.
* appleLangDefault() - Get the default locale string.
+ * appleMessageLoad() - Load a message catalog from a localizable bundle.
* cups_cache_lookup() - Lookup a language in the cache...
* cups_message_compare() - Compare two messages.
- * cups_unquote() - Unquote characters in strings...
+ * cups_message_free() - Free a message.
+ * cups_message_load() - Load the message catalog for a language.
+ * cups_unquote() - Unquote characters in strings...
*/
/*
* Include necessary headers...
*/
-#include "globals.h"
-#include "debug.h"
-#include <stdlib.h>
-#include <errno.h>
+#include "cups-private.h"
#ifdef HAVE_LANGINFO_H
# include <langinfo.h>
#endif /* HAVE_LANGINFO_H */
* Local globals...
*/
-#ifdef HAVE_PTHREAD_H
-static pthread_mutex_t lang_mutex = PTHREAD_MUTEX_INITIALIZER;
+static _cups_mutex_t lang_mutex = _CUPS_MUTEX_INITIALIZER;
/* Mutex to control access to cache */
-#endif /* HAVE_PTHREAD_H */
static cups_lang_t *lang_cache = NULL;
/* Language string cache */
-
-
-/*
- * Local functions...
- */
-
-#ifdef __APPLE__
-static const char *appleLangDefault(void);
-#endif /* __APPLE__ */
-static cups_lang_t *cups_cache_lookup(const char *name,
- cups_encoding_t encoding);
-static int cups_message_compare(_cups_message_t *m1,
- _cups_message_t *m2);
-static void cups_unquote(char *d, const char *s);
-
-
-/*
- * Local globals...
- */
-
static const char * const lang_encodings[] =
{ /* Encoding strings */
"us-ascii", "iso-8859-1",
"iso-8859-8", "iso-8859-9",
"iso-8859-10", "utf-8",
"iso-8859-13", "iso-8859-14",
- "iso-8859-15", "windows-874",
- "windows-1250", "windows-1251",
- "windows-1252", "windows-1253",
- "windows-1254", "windows-1255",
- "windows-1256", "windows-1257",
- "windows-1258", "koi8-r",
+ "iso-8859-15", "cp874",
+ "cp1250", "cp1251",
+ "cp1252", "cp1253",
+ "cp1254", "cp1255",
+ "cp1256", "cp1257",
+ "cp1258", "koi8-r",
"koi8-u", "iso-8859-11",
- "iso-8859-16", "mac-roman",
+ "iso-8859-16", "mac",
"unknown", "unknown",
"unknown", "unknown",
"unknown", "unknown",
"unknown", "unknown",
"unknown", "unknown",
"unknown", "unknown",
- "windows-932", "windows-936",
- "windows-949", "windows-950",
- "windows-1361", "unknown",
+ "cp932", "cp936",
+ "cp949", "cp950",
+ "cp1361", "unknown",
"unknown", "unknown",
"unknown", "unknown",
"unknown", "unknown",
"unknown", "unknown",
"unknown", "unknown",
"euc-cn", "euc-jp",
- "euc-kr", "euc-tw"
+ "euc-kr", "euc-tw",
+ "jis-x0213"
};
+#ifdef __APPLE__
+typedef struct
+{
+ const char * const language; /* Language ID */
+ const char * const locale; /* Locale ID */
+} _apple_language_locale_t;
+
+static const _apple_language_locale_t apple_language_locale[] =
+{ /* Locale to language ID LUT */
+ { "en", "en_US" },
+ { "nb", "no" },
+ { "zh-Hans", "zh_CN" },
+ { "zh-Hant", "zh_TW" }
+};
+#endif /* __APPLE__ */
+
+
+/*
+ * Local functions...
+ */
+
+
+#ifdef __APPLE__
+static const char *appleLangDefault(void);
+# ifdef CUPS_BUNDLEDIR
+# ifndef CF_RETURNS_RETAINED
+# if __has_feature(attribute_cf_returns_retained)
+# define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
+# else
+# define CF_RETURNS_RETAINED
+# endif /* __has_feature(attribute_cf_returns_retained) */
+# endif /* !CF_RETURNED_RETAINED */
+static cups_array_t *appleMessageLoad(const char *locale)
+ CF_RETURNS_RETAINED;
+# endif /* CUPS_BUNDLEDIR */
+#endif /* __APPLE__ */
+static cups_lang_t *cups_cache_lookup(const char *name,
+ cups_encoding_t encoding);
+static int cups_message_compare(_cups_message_t *m1,
+ _cups_message_t *m2);
+static void cups_message_free(_cups_message_t *m);
+static void cups_message_load(cups_lang_t *lang);
+static void cups_unquote(char *d, const char *s);
+
+
+#ifdef __APPLE__
+/*
+ * '_cupsAppleLanguage()' - Get the Apple language identifier associated with a
+ * locale ID.
+ */
+
+const char * /* O - Language ID */
+_cupsAppleLanguage(const char *locale, /* I - Locale ID */
+ char *language,/* I - Language ID buffer */
+ size_t langsize) /* I - Size of language ID buffer */
+{
+ int i; /* Looping var */
+ CFStringRef localeid, /* CF locale identifier */
+ langid; /* CF language identifier */
+
+
+ /*
+ * Copy the locale name and convert, as needed, to the Apple-specific
+ * locale identifier...
+ */
+
+ switch (strlen(locale))
+ {
+ default :
+ /*
+ * Invalid locale...
+ */
+
+ strlcpy(language, "en", langsize);
+ break;
+
+ case 2 :
+ strlcpy(language, locale, langsize);
+ break;
+
+ case 5 :
+ strlcpy(language, locale, langsize);
+
+ if (language[2] == '-')
+ {
+ /*
+ * Convert ll-cc to ll_CC...
+ */
+
+ language[2] = '_';
+ language[3] = toupper(language[3] & 255);
+ language[4] = toupper(language[4] & 255);
+ }
+ break;
+ }
+
+ for (i = 0;
+ i < (int)(sizeof(apple_language_locale) /
+ sizeof(apple_language_locale[0]));
+ i ++)
+ if (!strcmp(locale, apple_language_locale[i].locale))
+ {
+ strlcpy(language, apple_language_locale[i].language, sizeof(language));
+ break;
+ }
+
+ /*
+ * Attempt to map the locale ID to a language ID...
+ */
+
+ if ((localeid = CFStringCreateWithCString(kCFAllocatorDefault, language,
+ kCFStringEncodingASCII)) != NULL)
+ {
+ if ((langid = CFLocaleCreateCanonicalLanguageIdentifierFromString(
+ kCFAllocatorDefault, localeid)) != NULL)
+ {
+ CFStringGetCString(langid, language, langsize, kCFStringEncodingASCII);
+ CFRelease(langid);
+ }
+
+ CFRelease(localeid);
+ }
+
+ /*
+ * Return what we got...
+ */
+
+ return (language);
+}
+#endif /* __APPLE__ */
+
/*
* '_cupsEncodingName()' - Return the character encoding name string
{
if (encoding < 0 ||
encoding >= (sizeof(lang_encodings) / sizeof(const char *)))
+ {
+ DEBUG_printf(("1_cupsEncodingName(encoding=%d) = out of range (\"%s\")",
+ encoding, lang_encodings[0]));
return (lang_encodings[0]);
+ }
else
+ {
+ DEBUG_printf(("1_cupsEncodingName(encoding=%d) = \"%s\"",
+ encoding, lang_encodings[encoding]));
return (lang_encodings[encoding]);
+ }
}
* Free all languages in the cache...
*/
-#ifdef HAVE_PTHREAD_H
- pthread_mutex_lock(&lang_mutex);
-#endif /* HAVE_PTHREAD_H */
+ _cupsMutexLock(&lang_mutex);
for (lang = lang_cache; lang != NULL; lang = next)
{
lang_cache = NULL;
-#ifdef HAVE_PTHREAD_H
- pthread_mutex_unlock(&lang_mutex);
-#endif /* HAVE_PTHREAD_H */
+ _cupsMutexUnlock(&lang_mutex);
}
/*
* 'cupsLangFree()' - Free language data.
*
- * This does not actually free anything; use cupsLangFlush() for that.
+ * This does not actually free anything; use @link cupsLangFlush@ for that.
*/
void
cupsLangFree(cups_lang_t *lang) /* I - Language to free */
{
-#ifdef HAVE_PTHREAD_H
- pthread_mutex_lock(&lang_mutex);
-#endif /* HAVE_PTHREAD_H */
+ _cupsMutexLock(&lang_mutex);
if (lang != NULL && lang->used > 0)
lang->used --;
-#ifdef HAVE_PTHREAD_H
- pthread_mutex_unlock(&lang_mutex);
-#endif /* HAVE_PTHREAD_H */
+ _cupsMutexUnlock(&lang_mutex);
}
charset[16], /* Character set */
*csptr, /* Pointer to CODESET string */
*ptr, /* Pointer into language/charset */
- real[48], /* Real language name */
- filename[1024]; /* Filename for language locale file */
+ real[48]; /* Real language name */
cups_encoding_t encoding; /* Encoding to use */
cups_lang_t *lang; /* Current language... */
- _cups_globals_t *cg = _cupsGlobals();
- /* Pointer to library globals */
static const char * const locale_encodings[] =
{ /* Locale charset names */
"ASCII", "ISO88591", "ISO88592", "ISO88593",
"", "", "", "",
"", "", "", "",
- "EUCCN", "EUCJP", "EUCKR", "EUCTW"
+ "EUCCN", "EUCJP", "EUCKR", "EUCTW",
+ "SHIFT_JISX0213"
};
- DEBUG_printf(("cupsLangGet(language=\"%s\")\n", language ? language : "(null)"));
+ DEBUG_printf(("2cupsLangGet(language=\"%s\")", language));
#ifdef __APPLE__
/*
* Set the character set to UTF-8...
*/
- strcpy(charset, "UTF8");
+ strlcpy(charset, "UTF8", sizeof(charset));
/*
- * Apple's setlocale doesn't give us the user's localization
+ * Apple's setlocale doesn't give us the user's localization
* preference so we have to look it up this way...
*/
if (!language)
- language = appleLangDefault();
+ {
+ if (!getenv("SOFTWARE") || (language = getenv("LANG")) == NULL)
+ language = appleLangDefault();
+
+ DEBUG_printf(("4cupsLangGet: language=\"%s\"", language));
+ }
#else
/*
ptr = setlocale(LC_ALL, NULL);
# endif /* LC_MESSAGES */
- DEBUG_printf(("cupsLangGet: current locale is \"%s\"\n",
- ptr ? ptr : "(null)"));
+ DEBUG_printf(("4cupsLangGet: current locale is \"%s\"", ptr));
if (!ptr || !strcmp(ptr, "C") || !strcmp(ptr, "POSIX"))
{
*/
for (ptr = charset, csptr ++; *csptr; csptr ++)
- if (ptr < (charset + sizeof(charset) - 1) && isalnum(*csptr & 255))
+ if (ptr < (charset + sizeof(charset) - 1) && _cups_isalnum(*csptr))
*ptr++ = *csptr;
*ptr = '\0';
}
- else
- {
- /*
- * Default to UTF-8...
- */
-
- strcpy(charset, "UTF8");
- }
/*
* Get the locale for messages from the LC_MESSAGES locale setting...
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(("4cupsLangGet: new language value is \"%s\"", language));
}
}
#endif /* __APPLE__ */
*/
for (ptr = charset; *csptr; csptr ++)
- if (isalnum(*csptr & 255) && ptr < (charset + sizeof(charset) - 1))
+ if (_cups_isalnum(*csptr) && ptr < (charset + sizeof(charset) - 1))
*ptr++ = *csptr;
*ptr = '\0';
- DEBUG_printf(("cupsLangGet: charset set to \"%s\" via nl_langinfo(CODESET)...\n",
- charset));
+ DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via "
+ "nl_langinfo(CODESET)...", charset));
}
#endif /* CODESET */
+ /*
+ * If we don't have a character set by now, default to UTF-8...
+ */
+
+ if (!charset[0])
+ strlcpy(charset, "UTF8", sizeof(charset));
+
/*
* Parse the language string passed in to a locale string. "C" is the
* standard POSIX locale and is copied unchanged. Otherwise the
if (language == NULL || !language[0] ||
!strcmp(language, "POSIX"))
- strcpy(langname, "C");
+ strlcpy(langname, "C", sizeof(langname));
else
{
/*
*/
for (language ++, ptr = charset; *language; language ++)
- if (isalnum(*language & 255) && ptr < (charset + sizeof(charset) - 1))
+ if (_cups_isalnum(*language) && ptr < (charset + sizeof(charset) - 1))
*ptr++ = toupper(*language & 255);
*ptr = '\0';
if (strlen(langname) != 2)
{
- strcpy(langname, "C");
+ strlcpy(langname, "C", sizeof(langname));
country[0] = '\0';
charset[0] = '\0';
}
}
- DEBUG_printf(("cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"\n",
+ DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"",
langname, country, charset));
/*
for (i = 0;
i < (int)(sizeof(locale_encodings) / sizeof(locale_encodings[0]));
i ++)
- if (!strcasecmp(charset, locale_encodings[i]))
+ if (!_cups_strcasecmp(charset, locale_encodings[i]))
{
encoding = (cups_encoding_t)i;
break;
}
+
+ if (encoding == CUPS_AUTO_ENCODING)
+ {
+ /*
+ * Map alternate names for various character sets...
+ */
+
+ if (!_cups_strcasecmp(charset, "iso-2022-jp") ||
+ !_cups_strcasecmp(charset, "sjis"))
+ encoding = CUPS_WINDOWS_932;
+ else if (!_cups_strcasecmp(charset, "iso-2022-cn"))
+ encoding = CUPS_WINDOWS_936;
+ else if (!_cups_strcasecmp(charset, "iso-2022-kr"))
+ encoding = CUPS_WINDOWS_949;
+ else if (!_cups_strcasecmp(charset, "big5"))
+ encoding = CUPS_WINDOWS_950;
+ }
}
- DEBUG_printf(("cupsLangGet: encoding=%d(%s)\n", encoding,
+ DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding,
encoding == CUPS_AUTO_ENCODING ? "auto" :
lang_encodings[encoding]));
*/
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(langname, encoding)) != NULL)
- {
-#ifdef HAVE_PTHREAD_H
- pthread_mutex_unlock(&lang_mutex);
-#endif /* HAVE_PTHREAD_H */
+ strlcpy(real, langname, sizeof(real));
- return (lang);
- }
+ _cupsMutexLock(&lang_mutex);
- if (!country[0] || access(filename, 0))
+ if ((lang = cups_cache_lookup(real, encoding)) != NULL)
{
- /*
- * Country localization not available, look for generic localization...
- */
-
- snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
- langname, langname);
-
- if (access(filename, 0))
- {
- /*
- * No generic localization, so use POSIX...
- */
+ _cupsMutexUnlock(&lang_mutex);
- DEBUG_printf(("access(\"%s\", 0): %s\n", filename, strerror(errno)));
+ DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real));
- snprintf(filename, sizeof(filename), "%s/C/cups_C.po", cg->localedir);
- }
+ return (lang);
}
/*
if ((lang = calloc(sizeof(cups_lang_t), 1)) == NULL)
{
-#ifdef HAVE_PTHREAD_H
- pthread_mutex_unlock(&lang_mutex);
-#endif /* HAVE_PTHREAD_H */
+ _cupsMutexUnlock(&lang_mutex);
return (NULL);
}
*/
_cupsMessageFree(lang->strings);
+ lang->strings = NULL;
}
/*
else
lang->encoding = CUPS_UTF8;
- /*
- * Read the strings from the file...
- */
-
- lang->strings = _cupsMessageLoad(filename);
-
/*
* Return...
*/
-#ifdef HAVE_PTHREAD_H
- pthread_mutex_unlock(&lang_mutex);
-#endif /* HAVE_PTHREAD_H */
+ _cupsMutexUnlock(&lang_mutex);
return (lang);
}
_cupsLangString(cups_lang_t *lang, /* I - Language */
const char *message) /* I - Message */
{
+ const char *s; /* Localized message */
+
/*
* Range check input...
*/
- if (!lang || !message)
+ if (!lang || !message || !*message)
return (message);
-#ifdef HAVE_PTHREAD_H
- {
- const char *s; /* Localized message */
+ _cupsMutexLock(&lang_mutex);
- pthread_mutex_lock(&lang_mutex);
+ /*
+ * Load the message catalog if needed...
+ */
- s = _cupsMessageLookup(lang->strings, message);
+ if (!lang->strings)
+ cups_message_load(lang);
- pthread_mutex_unlock(&lang_mutex);
+ s = _cupsMessageLookup(lang->strings, message);
- return (s);
- }
-#else
- return (_cupsMessageLookup(lang->strings, message));
-#endif /* HAVE_PTHREAD_H */
+ _cupsMutexUnlock(&lang_mutex);
+
+ return (s);
}
void
_cupsMessageFree(cups_array_t *a) /* I - Message array */
{
- _cups_message_t *m; /* Current message */
-
-
- for (m = (_cups_message_t *)cupsArrayFirst(a);
- m;
- m = (_cups_message_t *)cupsArrayNext(a))
- {
- /*
- * Remove the message from the array, then free the message and strings.
- */
-
- cupsArrayRemove(a, m);
-
- if (m->id)
- free(m->id);
+#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
+ /*
+ * Release the cups.strings dictionary as needed...
+ */
- if (m->str)
- free(m->str);
-
- free(m);
- }
+ if (cupsArrayUserData(a))
+ CFRelease((CFDictionaryRef)cupsArrayUserData(a));
+#endif /* __APPLE__ && CUPS_BUNDLEDIR */
/*
* Free the array...
*/
cups_array_t * /* O - New message array */
-_cupsMessageLoad(const char *filename) /* I - Message catalog to load */
+_cupsMessageLoad(const char *filename, /* I - Message catalog to load */
+ int unquote) /* I - Unescape \foo in strings? */
{
cups_file_t *fp; /* Message file */
cups_array_t *a; /* Message array */
*ptr, /* Pointer into buffer */
*temp; /* New string */
int length; /* Length of combined strings */
+ size_t ptrlen; /* Length of string */
- DEBUG_printf(("_cupsMessageLoad(filename=\"%s\")\n", filename));
+ DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename));
/*
* Create an array to hold the messages...
*/
- if ((a = cupsArrayNew((cups_array_func_t)cups_message_compare, NULL)) == NULL)
+ if ((a = _cupsMessageNew(NULL)) == NULL)
+ {
+ DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
return (NULL);
+ }
/*
* Open the message catalog file...
*/
if ((fp = cupsFileOpen(filename, "r")) == NULL)
+ {
+ DEBUG_printf(("5_cupsMessageLoad: Unable to open file: %s",
+ strerror(errno)));
return (a);
+ }
/*
* Read messages from the catalog file until EOF...
/*
* Find start of value...
*/
-
+
if ((ptr = strchr(s, '\"')) == NULL)
continue;
* Unquote the text...
*/
- cups_unquote(ptr, ptr);
+ if (unquote)
+ cups_unquote(ptr, ptr);
/*
* Create or add to a message...
*/
if (m)
- cupsArrayAdd(a, m);
+ {
+ if (m->str && m->str[0])
+ {
+ cupsArrayAdd(a, m);
+ }
+ else
+ {
+ /*
+ * Translation is empty, don't add it... (STR #4033)
+ */
+
+ free(m->id);
+ if (m->str)
+ free(m->str);
+ free(m);
+ }
+ }
/*
* Create a new message with the given msgid string...
return (a);
}
- m->id = strdup(ptr);
+ if ((m->id = strdup(ptr)) == NULL)
+ {
+ free(m);
+ cupsFileClose(fp);
+ return (a);
+ }
}
else if (s[0] == '\"' && m)
{
* Append to current string...
*/
- length = strlen(m->str ? m->str : m->id);
+ length = (int)strlen(m->str ? m->str : m->id);
+ ptrlen = strlen(ptr);
if ((temp = realloc(m->str ? m->str : m->id,
- length + strlen(ptr) + 1)) == NULL)
+ length + ptrlen + 1)) == NULL)
{
+ if (m->str)
+ free(m->str);
+ free(m->id);
+ free(m);
+
cupsFileClose(fp);
return (a);
}
{
/*
* Copy the new portion to the end of the msgstr string - safe
- * to use strcpy because the buffer is allocated to the correct
+ * to use memcpy because the buffer is allocated to the correct
* size...
*/
m->str = temp;
- strcpy(m->str + length, ptr);
+ memcpy(m->str + length, ptr, ptrlen + 1);
}
else
{
/*
* Copy the new portion to the end of the msgid string - safe
- * to use strcpy because the buffer is allocated to the correct
+ * to use memcpy because the buffer is allocated to the correct
* size...
*/
m->id = temp;
- strcpy(m->id + length, ptr);
+ memcpy(m->id + length, ptr, ptrlen + 1);
}
}
else if (!strncmp(s, "msgstr", 6) && m)
* Set the string...
*/
- m->str = strdup(ptr);
+ if ((m->str = strdup(ptr)) == NULL)
+ {
+ free(m->id);
+ free(m);
+
+ cupsFileClose(fp);
+ return (a);
+ }
}
}
*/
if (m)
- cupsArrayAdd(a, m);
+ {
+ if (m->str && m->str[0])
+ {
+ cupsArrayAdd(a, m);
+ }
+ else
+ {
+ /*
+ * Translation is empty, don't add it... (STR #4033)
+ */
+
+ free(m->id);
+ if (m->str)
+ free(m->str);
+ free(m);
+ }
+ }
/*
* Close the message catalog file and return the new array...
cupsFileClose(fp);
+ DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
+ cupsArrayCount(a)));
+
return (a);
}
key.id = (char *)m;
match = (_cups_message_t *)cupsArrayFind(a, &key);
+#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
+ if (!match && cupsArrayUserData(a))
+ {
+ /*
+ * Try looking the string up in the cups.strings dictionary...
+ */
+
+ CFDictionaryRef dict; /* cups.strings dictionary */
+ CFStringRef cfm, /* Message as a CF string */
+ cfstr; /* Localized text as a CF string */
+
+ dict = (CFDictionaryRef)cupsArrayUserData(a);
+ cfm = CFStringCreateWithCString(kCFAllocatorDefault, m,
+ kCFStringEncodingUTF8);
+ match = calloc(1, sizeof(_cups_message_t));
+ match->id = strdup(m);
+ cfstr = cfm ? CFDictionaryGetValue(dict, cfm) : NULL;
+
+ if (cfstr)
+ {
+ char buffer[1024]; /* Message buffer */
+
+ CFStringGetCString(cfstr, buffer, sizeof(buffer), kCFStringEncodingUTF8);
+ match->str = strdup(buffer);
+
+ DEBUG_printf(("1_cupsMessageLookup: Found \"%s\" as \"%s\"...",
+ m, buffer));
+ }
+ else
+ {
+ match->str = strdup(m);
+
+ DEBUG_printf(("1_cupsMessageLookup: Did not find \"%s\"...", m));
+ }
+
+ cupsArrayAdd(a, match);
+
+ if (cfm)
+ CFRelease(cfm);
+ }
+#endif /* __APPLE__ && CUPS_BUNDLEDIR */
+
if (match && match->str)
return (match->str);
else
}
-#ifdef __APPLE__
/*
- * Code & data to translate OSX's language names to their ISO 639-1 locale.
- *
- * The first version uses the new CoreFoundation API added in 10.3 (Panther),
- * the second is for 10.2 (Jaguar).
+ * '_cupsMessageNew()' - Make a new message catalog array.
*/
-# ifdef HAVE_CF_LOCALE_ID
+cups_array_t * /* O - Array */
+_cupsMessageNew(void *context) /* I - User data */
+{
+ return (cupsArrayNew3((cups_array_func_t)cups_message_compare, context,
+ (cups_ahash_func_t)NULL, 0,
+ (cups_acopy_func_t)NULL,
+ (cups_afree_func_t)cups_message_free));
+}
+
+
+#ifdef __APPLE__
/*
* 'appleLangDefault()' - Get the default locale string.
*/
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 */
+ DEBUG_puts("2appleLangDefault()");
+
/*
* Only do the lookup and translation the first time.
*/
if (!cg->language[0])
{
- localizationList =
- CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
- kCFPreferencesCurrentApplication);
+ if (getenv("SOFTWARE") != NULL && (lang = getenv("LANG")) != NULL)
+ {
+ DEBUG_printf(("3appleLangDefault: Using LANG=%s", lang));
+ strlcpy(cg->language, lang, sizeof(cg->language));
+ return (cg->language);
+ }
+ else if ((bundle = CFBundleGetMainBundle()) != NULL &&
+ (bundleList = CFBundleCopyBundleLocalizations(bundle)) != NULL)
+ {
+ DEBUG_puts("3appleLangDefault: Getting localizationList from bundle.");
+
+ localizationList =
+ CFBundleCopyPreferredLocalizationsFromArray(bundleList);
+
+ CFRelease(bundleList);
+ }
+ else
+ {
+ DEBUG_puts("3appleLangDefault: Getting localizationList from preferences.");
+
+ localizationList =
+ CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
+ kCFPreferencesCurrentApplication);
+ }
- if (localizationList != NULL)
+ if (localizationList)
{
+
+#ifdef DEBUG
+ if (CFGetTypeID(localizationList) == CFArrayGetTypeID())
+ DEBUG_printf(("3appleLangDefault: Got localizationList, %d entries.",
+ (int)CFArrayGetCount(localizationList)));
+ else
+ DEBUG_puts("3appleLangDefault: Got localizationList but not an array.");
+#endif /* DEBUG */
+
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(("3appleLangDefault: cg->language=\"%s\"",
+ cg->language));
+
+ /*
+ * Map new language identifiers to locales...
+ */
+
+ for (i = 0;
+ i < (int)(sizeof(apple_language_locale) /
+ sizeof(apple_language_locale[0]));
+ i ++)
+ {
+ if (!strcmp(cg->language, apple_language_locale[i].language))
+ {
+ DEBUG_printf(("3appleLangDefault: mapping \"%s\" to \"%s\"...",
+ cg->language, apple_language_locale[i].locale));
+ strlcpy(cg->language, apple_language_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));
}
- }
+ else
+ DEBUG_puts("3appleLangDefault: Unable to get localeName.");
+ }
}
CFRelease(localizationList);
}
-
+
/*
* If we didn't find the language, default to en_US...
*/
if (!cg->language[0])
+ {
+ DEBUG_puts("3appleLangDefault: Defaulting to en_US.");
strlcpy(cg->language, "en_US.UTF-8", sizeof(cg->language));
+ }
}
/*
return (cg->language);
}
-# else
-/*
- * Code & data to translate OSX 10.2's language names to their ISO 639-1
- * locale.
- */
-
-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[] =
-{
- { "English" , "en_US.UTF-8" }, { "French" , "fr.UTF-8" },
- { "German" , "de.UTF-8" }, { "Italian" , "it.UTF-8" },
- { "Dutch" , "nl.UTF-8" }, { "Swedish" , "sv.UTF-8" },
- { "Spanish" , "es.UTF-8" }, { "Danish" , "da.UTF-8" },
- { "Portuguese" , "pt.UTF-8" }, { "Norwegian" , "no.UTF-8" },
- { "Hebrew" , "he.UTF-8" }, { "Japanese" , "ja.UTF-8" },
- { "Arabic" , "ar.UTF-8" }, { "Finnish" , "fi.UTF-8" },
- { "Greek" , "el.UTF-8" }, { "Icelandic" , "is.UTF-8" },
- { "Maltese" , "mt.UTF-8" }, { "Turkish" , "tr.UTF-8" },
- { "Croatian" , "hr.UTF-8" }, { "Chinese" , "zh.UTF-8" },
- { "Urdu" , "ur.UTF-8" }, { "Hindi" , "hi.UTF-8" },
- { "Thai" , "th.UTF-8" }, { "Korean" , "ko.UTF-8" },
- { "Lithuanian" , "lt.UTF-8" }, { "Polish" , "pl.UTF-8" },
- { "Hungarian" , "hu.UTF-8" }, { "Estonian" , "et.UTF-8" },
- { "Latvian" , "lv.UTF-8" }, { "Sami" , "se.UTF-8" },
- { "Faroese" , "fo.UTF-8" }, { "Farsi" , "fa.UTF-8" },
- { "Russian" , "ru.UTF-8" }, { "Chinese" , "zh.UTF-8" },
- { "Dutch" , "nl.UTF-8" }, { "Irish" , "ga.UTF-8" },
- { "Albanian" , "sq.UTF-8" }, { "Romanian" , "ro.UTF-8" },
- { "Czech" , "cs.UTF-8" }, { "Slovak" , "sk.UTF-8" },
- { "Slovenian" , "sl.UTF-8" }, { "Yiddish" , "yi.UTF-8" },
- { "Serbian" , "sr.UTF-8" }, { "Macedonian" , "mk.UTF-8" },
- { "Bulgarian" , "bg.UTF-8" }, { "Ukrainian" , "uk.UTF-8" },
- { "Byelorussian", "be.UTF-8" }, { "Uzbek" , "uz.UTF-8" },
- { "Kazakh" , "kk.UTF-8" }, { "Azerbaijani", "az.UTF-8" },
- { "Azerbaijani" , "az.UTF-8" }, { "Armenian" , "hy.UTF-8" },
- { "Georgian" , "ka.UTF-8" }, { "Moldavian" , "mo.UTF-8" },
- { "Kirghiz" , "ky.UTF-8" }, { "Tajiki" , "tg.UTF-8" },
- { "Turkmen" , "tk.UTF-8" }, { "Mongolian" , "mn.UTF-8" },
- { "Mongolian" , "mn.UTF-8" }, { "Pashto" , "ps.UTF-8" },
- { "Kurdish" , "ku.UTF-8" }, { "Kashmiri" , "ks.UTF-8" },
- { "Sindhi" , "sd.UTF-8" }, { "Tibetan" , "bo.UTF-8" },
- { "Nepali" , "ne.UTF-8" }, { "Sanskrit" , "sa.UTF-8" },
- { "Marathi" , "mr.UTF-8" }, { "Bengali" , "bn.UTF-8" },
- { "Assamese" , "as.UTF-8" }, { "Gujarati" , "gu.UTF-8" },
- { "Punjabi" , "pa.UTF-8" }, { "Oriya" , "or.UTF-8" },
- { "Malayalam" , "ml.UTF-8" }, { "Kannada" , "kn.UTF-8" },
- { "Tamil" , "ta.UTF-8" }, { "Telugu" , "te.UTF-8" },
- { "Sinhalese" , "si.UTF-8" }, { "Burmese" , "my.UTF-8" },
- { "Khmer" , "km.UTF-8" }, { "Lao" , "lo.UTF-8" },
- { "Vietnamese" , "vi.UTF-8" }, { "Indonesian" , "id.UTF-8" },
- { "Tagalog" , "tl.UTF-8" }, { "Malay" , "ms.UTF-8" },
- { "Malay" , "ms.UTF-8" }, { "Amharic" , "am.UTF-8" },
- { "Tigrinya" , "ti.UTF-8" }, { "Oromo" , "om.UTF-8" },
- { "Somali" , "so.UTF-8" }, { "Swahili" , "sw.UTF-8" },
- { "Kinyarwanda" , "rw.UTF-8" }, { "Rundi" , "rn.UTF-8" },
- { "Nyanja" , "" }, { "Malagasy" , "mg.UTF-8" },
- { "Esperanto" , "eo.UTF-8" }, { "Welsh" , "cy.UTF-8" },
- { "Basque" , "eu.UTF-8" }, { "Catalan" , "ca.UTF-8" },
- { "Latin" , "la.UTF-8" }, { "Quechua" , "qu.UTF-8" },
- { "Guarani" , "gn.UTF-8" }, { "Aymara" , "ay.UTF-8" },
- { "Tatar" , "tt.UTF-8" }, { "Uighur" , "ug.UTF-8" },
- { "Dzongkha" , "dz.UTF-8" }, { "Javanese" , "jv.UTF-8" },
- { "Sundanese" , "su.UTF-8" }, { "Galician" , "gl.UTF-8" },
- { "Afrikaans" , "af.UTF-8" }, { "Breton" , "br.UTF-8" },
- { "Inuktitut" , "iu.UTF-8" }, { "Scottish" , "gd.UTF-8" },
- { "Manx" , "gv.UTF-8" }, { "Irish" , "ga.UTF-8" },
- { "Tongan" , "to.UTF-8" }, { "Greek" , "el.UTF-8" },
- { "Greenlandic" , "kl.UTF-8" }, { "Azerbaijani", "az.UTF-8" }
-};
+# ifdef CUPS_BUNDLEDIR
/*
- * 'appleLangDefault()' - Get the default locale string.
+ * 'appleMessageLoad()' - Load a message catalog from a localizable bundle.
*/
-static const char * /* O - Locale string */
-appleLangDefault(void)
+static cups_array_t * /* O - Message catalog */
+appleMessageLoad(const char *locale) /* I - Locale ID */
{
- int i; /* Looping var */
- CFPropertyListRef localizationList;
- /* List of localization data */
- CFStringRef localizationName;
- /* Current name */
- char buff[256]; /* Temporary buffer */
- _cups_globals_t *cg = _cupsGlobals();
- /* Pointer to library globals */
+ char filename[1024], /* Path to cups.strings file */
+ applelang[256]; /* Apple language ID */
+ CFURLRef url; /* URL to cups.strings file */
+ CFReadStreamRef stream = NULL; /* File stream */
+ CFPropertyListRef plist = NULL; /* Localization file */
+#ifdef DEBUG
+ CFErrorRef error = NULL; /* Error when opening file */
+#endif /* DEBUG */
+ DEBUG_printf(("appleMessageLoad(locale=\"%s\")", locale));
+
/*
- * Only do the lookup and translation the first time.
+ * Load the cups.strings file...
*/
- if (cg->language == NULL)
+ snprintf(filename, sizeof(filename),
+ CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings",
+ _cupsAppleLanguage(locale, applelang, sizeof(applelang)));
+ DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename));
+
+ if (access(filename, 0))
{
- localizationList =
- CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
- kCFPreferencesCurrentApplication);
+ /*
+ * Try alternate lproj directory names...
+ */
- if (localizationList != NULL)
+ if (!strncmp(locale, "en", 2))
+ locale = "English";
+ else if (!strncmp(locale, "nb", 2) || !strncmp(locale, "nl", 2))
+ locale = "Dutch";
+ else if (!strncmp(locale, "fr", 2))
+ locale = "French";
+ else if (!strncmp(locale, "de", 2))
+ locale = "German";
+ else if (!strncmp(locale, "it", 2))
+ locale = "Italian";
+ else if (!strncmp(locale, "ja", 2))
+ locale = "Japanese";
+ else if (!strncmp(locale, "es", 2))
+ locale = "Spanish";
+
+ snprintf(filename, sizeof(filename),
+ CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", locale);
+ DEBUG_printf(("1appleMessageLoad: alternate filename=\"%s\"", filename));
+ }
+
+ url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+ (UInt8 *)filename,
+ strlen(filename), false);
+ if (url)
+ {
+ stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
+ if (stream)
{
- if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
- CFArrayGetCount(localizationList) > 0)
+ /*
+ * Read the property list containing the localization data.
+ *
+ * NOTE: This code currently generates a clang "potential leak"
+ * warning, but the object is released in _cupsMessageFree().
+ */
+
+ CFReadStreamOpen(stream);
+
+#ifdef DEBUG
+ plist = CFPropertyListCreateWithStream(kCFAllocatorDefault, stream, 0,
+ kCFPropertyListImmutable, NULL,
+ &error);
+ if (error)
{
- localizationName = CFArrayGetValueAtIndex(localizationList, 0);
+ CFStringRef msg = CFErrorCopyDescription(error);
+ /* Error message */
- if (localizationName != NULL &&
- CFGetTypeID(localizationName) == CFStringGetTypeID())
- {
- CFIndex length = CFStringGetLength(localizationName);
+ CFStringGetCString(msg, filename, sizeof(filename),
+ kCFStringEncodingUTF8);
+ DEBUG_printf(("1appleMessageLoad: %s", filename));
- if (length <= sizeof(buff) &&
- CFStringGetCString(localizationName, buff, sizeof(buff),
- kCFStringEncodingASCII))
- {
- buff[sizeof(buff) - 1] = '\0';
+ CFRelease(msg);
+ CFRelease(error);
+ }
- for (i = 0;
- i < sizeof(apple_name_locale) / sizeof(apple_name_locale[0]);
- i++)
- {
- if (!strcasecmp(buff, apple_name_locale[i].name))
- {
- cg->language = apple_name_locale[i].locale;
- break;
- }
- }
- }
- }
+#else
+ plist = CFPropertyListCreateWithStream(kCFAllocatorDefault, stream, 0,
+ kCFPropertyListImmutable, NULL,
+ NULL);
+#endif /* DEBUG */
+
+ if (plist && CFGetTypeID(plist) != CFDictionaryGetTypeID())
+ {
+ CFRelease(plist);
+ plist = NULL;
}
- CFRelease(localizationList);
+ CFRelease(stream);
}
-
- /*
- * If we didn't find the language, default to en_US...
- */
- if (cg->language == NULL)
- cg->language = apple_name_locale[0].locale;
+ CFRelease(url);
}
+ DEBUG_printf(("1appleMessageLoad: url=%p, stream=%p, plist=%p", url, stream,
+ plist));
+
/*
- * Return the cached locale...
+ * Create and return an empty array to act as a cache for messages, passing the
+ * plist as the user data.
*/
- return (cg->language);
+ return (_cupsMessageNew((void *)plist));
}
-# endif /* HAVE_CF_LOCALE_ID */
+# endif /* CUPS_BUNDLEDIR */
#endif /* __APPLE__ */
*/
static cups_lang_t * /* O - Language data or NULL */
-cups_cache_lookup(const char *name,/* I - Name of locale */
- cups_encoding_t encoding)
- /* I - Encoding of locale */
+cups_cache_lookup(
+ const char *name, /* I - Name of locale */
+ cups_encoding_t encoding) /* I - Encoding of locale */
{
cups_lang_t *lang; /* Current language */
- DEBUG_printf(("cups_cache_lookup(name=\"%s\", encoding=%d(%s))\n", name,
+ DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name,
encoding, encoding == CUPS_AUTO_ENCODING ? "auto" :
lang_encodings[encoding]));
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,
+ DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
+ "encoding=%d(%s)", lang, lang->language, lang->encoding,
lang_encodings[lang->encoding]));
if (!strcmp(lang->language, name) &&
{
lang->used ++;
- DEBUG_puts("cups_cache_lookup: returning match!");
+ DEBUG_puts("8cups_cache_lookup: returning match!");
return (lang);
}
}
- DEBUG_puts("cups_cache_lookup: returning NULL!");
+ DEBUG_puts("8cups_cache_lookup: returning NULL!");
return (NULL);
}
}
+/*
+ * 'cups_message_free()' - Free a message.
+ */
+
+static void
+cups_message_free(_cups_message_t *m) /* I - Message */
+{
+ if (m->id)
+ free(m->id);
+
+ if (m->str)
+ free(m->str);
+
+ free(m);
+}
+
+
+/*
+ * 'cups_message_load()' - Load the message catalog for a language.
+ */
+
+static void
+cups_message_load(cups_lang_t *lang) /* I - Language */
+{
+#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
+ lang->strings = appleMessageLoad(lang->language);
+
+#else
+ char filename[1024]; /* Filename for language locale file */
+ _cups_globals_t *cg = _cupsGlobals();
+ /* Pointer to library globals */
+
+
+ snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
+ lang->language, lang->language);
+
+ if (strchr(lang->language, '_') && access(filename, 0))
+ {
+ /*
+ * Country localization not available, look for generic localization...
+ */
+
+ snprintf(filename, sizeof(filename), "%s/%.2s/cups_%.2s.po", cg->localedir,
+ lang->language, lang->language);
+
+ if (access(filename, 0))
+ {
+ /*
+ * No generic localization, so use POSIX...
+ */
+
+ DEBUG_printf(("4cups_message_load: access(\"%s\", 0): %s", filename,
+ strerror(errno)));
+
+ snprintf(filename, sizeof(filename), "%s/C/cups_C.po", cg->localedir);
+ }
+ }
+
+ /*
+ * Read the strings from the file...
+ */
+
+ lang->strings = _cupsMessageLoad(filename, 1);
+#endif /* __APPLE__ && CUPS_BUNDLEDIR */
+}
+
+
/*
* 'cups_unquote()' - Unquote characters in strings...
*/
*d = *d * 8 + *s - '0';
s ++;
}
+
+ d ++;
}
else
{
/*
- * End of "$Id: language.c 5373 2006-04-06 20:03:32Z mike $".
+ * End of "$Id$".
*/