/*
- * "$Id: language.c 7558 2008-05-12 23:46:44Z mike $"
+ * I18N/language support for CUPS.
*
- * I18N/language support for CUPS.
+ * Copyright 2007-2017 by Apple Inc.
+ * Copyright 1997-2007 by Easy Software Products.
*
- * Copyright 2007-2011 by Apple Inc.
- * Copyright 1997-2007 by Easy Software Products.
- *
- * These coded instructions, statements, and computer programs are the
- * 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:
- *
- * _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.
- * _cupsMessageFree() - Free a messages array.
- * _cupsMessageLoad() - Load a .po file into a messages array.
- * _cupsMessageLookup() - Lookup a message string.
- * 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_message_free() - Free a message.
- * cups_unquote() - Unquote characters in strings...
+ * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
*/
/*
#ifdef HAVE_LANGINFO_H
# include <langinfo.h>
#endif /* HAVE_LANGINFO_H */
-#ifdef WIN32
+#ifdef _WIN32
# include <io.h>
#else
# include <unistd.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
#ifdef HAVE_COREFOUNDATION_H
# include <CoreFoundation/CoreFoundation.h>
#endif /* HAVE_COREFOUNDATION_H */
"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",
"euc-cn", "euc-jp",
"euc-kr", "euc-tw",
- "jis-x0213"
+ "shift_jisx0213"
};
#ifdef __APPLE__
} _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" }
+{ /* Language to locale ID LUT */
+ { "en", "en_US" },
+ { "nb", "no" },
+ { "nb_NO", "no" },
+ { "zh-Hans", "zh_CN" },
+ { "zh_HANS", "zh_CN" },
+ { "zh-Hant", "zh_TW" },
+ { "zh_HANT", "zh_TW" },
+ { "zh-Hant_CN", "zh_TW" }
};
#endif /* __APPLE__ */
* Local functions...
*/
+
#ifdef __APPLE__
static const char *appleLangDefault(void);
# ifdef CUPS_BUNDLEDIR
-static cups_array_t *appleMessageLoad(const char *locale);
+# 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 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_message_puts(cups_file_t *fp, const char *s);
+static int cups_read_strings(cups_file_t *fp, int flags, cups_array_t *a);
static void cups_unquote(char *d, const char *s);
*/
language[2] = '_';
- language[3] = toupper(language[3] & 255);
- language[4] = toupper(language[4] & 255);
+ language[3] = (char)toupper(language[3] & 255);
+ language[4] = (char)toupper(language[4] & 255);
}
break;
}
if ((langid = CFLocaleCreateCanonicalLanguageIdentifierFromString(
kCFAllocatorDefault, localeid)) != NULL)
{
- CFStringGetCString(langid, language, langsize, kCFStringEncodingASCII);
+ CFStringGetCString(langid, language, (CFIndex)langsize, kCFStringEncodingASCII);
CFRelease(langid);
}
return (language);
}
+
+
+/*
+ * '_cupsAppleLocale()' - Get the locale associated with an Apple language ID.
+ */
+
+const char * /* O - Locale */
+_cupsAppleLocale(CFStringRef languageName, /* I - Apple language ID */
+ char *locale, /* I - Buffer for locale */
+ size_t localesize) /* I - Size of buffer */
+{
+ int i; /* Looping var */
+ CFStringRef localeName; /* Locale as a CF string */
+#ifdef DEBUG
+ char temp[1024]; /* Temporary string */
+
+
+ if (!CFStringGetCString(languageName, temp, (CFIndex)sizeof(temp), kCFStringEncodingASCII))
+ temp[0] = '\0';
+
+ DEBUG_printf(("_cupsAppleLocale(languageName=%p(%s), locale=%p, localsize=%d)", (void *)languageName, temp, (void *)locale, (int)localesize));
+#endif /* DEBUG */
+
+ localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorDefault, languageName);
+
+ if (localeName)
+ {
+ /*
+ * Copy the locale name and tweak as needed...
+ */
+
+ if (!CFStringGetCString(localeName, locale, (CFIndex)localesize, kCFStringEncodingASCII))
+ *locale = '\0';
+
+ DEBUG_printf(("_cupsAppleLocale: locale=\"%s\"", locale));
+
+ CFRelease(localeName);
+
+ /*
+ * Map new language identifiers to locales...
+ */
+
+ for (i = 0;
+ i < (int)(sizeof(apple_language_locale) /
+ sizeof(apple_language_locale[0]));
+ i ++)
+ {
+ size_t len = strlen(apple_language_locale[i].language);
+
+ if (!strcmp(locale, apple_language_locale[i].language) ||
+ (!strncmp(locale, apple_language_locale[i].language, len) && (locale[len] == '_' || locale[len] == '-')))
+ {
+ DEBUG_printf(("_cupsAppleLocale: Updating locale to \"%s\".", apple_language_locale[i].locale));
+ strlcpy(locale, apple_language_locale[i].locale, localesize);
+ break;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Just try the Apple language name...
+ */
+
+ if (!CFStringGetCString(languageName, locale, (CFIndex)localesize, kCFStringEncodingASCII))
+ *locale = '\0';
+ }
+
+ if (!*locale)
+ {
+ DEBUG_puts("_cupsAppleLocale: Returning NULL.");
+ return (NULL);
+ }
+
+ /*
+ * Convert language subtag into region subtag...
+ */
+
+ if (locale[2] == '-')
+ locale[2] = '_';
+ else if (locale[3] == '-')
+ locale[3] = '_';
+
+ if (!strchr(locale, '.'))
+ strlcat(locale, ".UTF-8", localesize);
+
+ DEBUG_printf(("_cupsAppleLocale: Returning \"%s\".", locale));
+
+ return (locale);
+}
#endif /* __APPLE__ */
_cupsEncodingName(
cups_encoding_t encoding) /* I - Encoding value */
{
- if (encoding < 0 ||
- encoding >= (sizeof(lang_encodings) / sizeof(const char *)))
+ if (encoding < CUPS_US_ASCII ||
+ encoding >= (cups_encoding_t)(sizeof(lang_encodings) / sizeof(lang_encodings[0])))
{
DEBUG_printf(("1_cupsEncodingName(encoding=%d) = out of range (\"%s\")",
encoding, lang_encodings[0]));
real[48]; /* Real language name */
cups_encoding_t encoding; /* Encoding to use */
cups_lang_t *lang; /* Current language... */
-#if !defined(__APPLE__) || !defined(CUPS_BUNDLEDIR)
- char filename[1024]; /* Filename for language locale file */
- _cups_globals_t *cg = _cupsGlobals();
- /* Pointer to library globals */
-#endif /* !__APPLE__ || !CUPS_BUNDLEDIR */
static const char * const locale_encodings[] =
{ /* Locale charset names */
"ASCII", "ISO88591", "ISO88592", "ISO88593",
* 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 (!charset[0])
- strcpy(charset, "UTF8");
+ strlcpy(charset, "UTF8", sizeof(charset));
/*
* Parse the language string passed in to a locale string. "C" is the
if (language == NULL || !language[0] ||
!strcmp(language, "POSIX"))
- strcpy(langname, "C");
+ strlcpy(langname, "C", sizeof(langname));
else
{
/*
if (*language == '_' || *language == '-' || *language == '.')
break;
else if (ptr < (langname + sizeof(langname) - 1))
- *ptr++ = tolower(*language & 255);
+ *ptr++ = (char)tolower(*language & 255);
*ptr = '\0';
if (*language == '.')
break;
else if (ptr < (country + sizeof(country) - 1))
- *ptr++ = toupper(*language & 255);
+ *ptr++ = (char)toupper(*language & 255);
*ptr = '\0';
+
+ /*
+ * Map Chinese region codes to legacy country codes.
+ */
+
+ if (!strcmp(language, "zh") && !strcmp(country, "HANS"))
+ strlcpy(country, "CN", sizeof(country));
+ if (!strcmp(language, "zh") && !strcmp(country, "HANT"))
+ strlcpy(country, "TW", sizeof(country));
}
if (*language == '.' && !charset[0])
for (language ++, ptr = charset; *language; language ++)
if (_cups_isalnum(*language) && ptr < (charset + sizeof(charset) - 1))
- *ptr++ = toupper(*language & 255);
+ *ptr++ = (char)toupper(*language & 255);
*ptr = '\0';
}
* Force a POSIX locale for an invalid language name...
*/
- if (strlen(langname) != 2)
+ if (strlen(langname) != 2 && strlen(langname) != 3)
{
- strcpy(langname, "C");
+ strlcpy(langname, "C", sizeof(langname));
country[0] = '\0';
charset[0] = '\0';
}
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;
* Map alternate names for various character sets...
*/
- if (!strcasecmp(charset, "iso-2022-jp") ||
- !strcasecmp(charset, "sjis"))
+ if (!_cups_strcasecmp(charset, "iso-2022-jp") ||
+ !_cups_strcasecmp(charset, "sjis"))
encoding = CUPS_WINDOWS_932;
- else if (!strcasecmp(charset, "iso-2022-cn"))
+ else if (!_cups_strcasecmp(charset, "iso-2022-cn"))
encoding = CUPS_WINDOWS_936;
- else if (!strcasecmp(charset, "iso-2022-kr"))
+ else if (!_cups_strcasecmp(charset, "iso-2022-kr"))
encoding = CUPS_WINDOWS_949;
- else if (!strcasecmp(charset, "big5"))
+ else if (!_cups_strcasecmp(charset, "big5"))
encoding = CUPS_WINDOWS_950;
}
}
*/
if (country[0])
- {
snprintf(real, sizeof(real), "%s_%s", langname, country);
-
-#if !defined(__APPLE__) || !defined(CUPS_BUNDLEDIR)
- snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
- real, real);
-#endif /* !__APPLE__ || !CUPS_BUNDLEDIR */
- }
else
- {
- strcpy(real, langname);
-#if !defined(__APPLE__) || !defined(CUPS_BUNDLEDIR)
- filename[0] = '\0'; /* anti-compiler-warning-code */
-#endif /* !__APPLE__ || !CUPS_BUNDLEDIR */
- }
+ strlcpy(real, langname, sizeof(real));
_cupsMutexLock(&lang_mutex);
return (lang);
}
-#if !defined(__APPLE__) || !defined(CUPS_BUNDLEDIR)
- if (!country[0] || access(filename, 0))
- {
- /*
- * 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...
- */
-
- DEBUG_printf(("4cupsLangGet: access(\"%s\", 0): %s", filename,
- strerror(errno)));
-
- snprintf(filename, sizeof(filename), "%s/C/cups_C.po", cg->localedir);
- }
- }
-#endif /* !__APPLE__ || !CUPS_BUNDLEDIR */
-
/*
* See if there is a free language available; if so, use that
* record...
*/
_cupsMessageFree(lang->strings);
+ lang->strings = NULL;
}
/*
else
lang->encoding = CUPS_UTF8;
- /*
- * Read the strings from the file...
- */
-
-#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
- lang->strings = appleMessageLoad(lang->language);
-#else
- lang->strings = _cupsMessageLoad(filename, 1);
-#endif /* __APPLE__ && CUPS_BUNDLEDIR */
-
/*
* Return...
*/
{
const char *s; /* Localized message */
+
+ DEBUG_printf(("_cupsLangString(lang=%p, message=\"%s\")", (void *)lang, message));
+
/*
* Range check input...
*/
- if (!lang || !message)
+ if (!lang || !message || !*message)
return (message);
_cupsMutexLock(&lang_mutex);
+ /*
+ * Load the message catalog if needed...
+ */
+
+ if (!lang->strings)
+ cups_message_load(lang);
+
s = _cupsMessageLookup(lang->strings, message);
_cupsMutexUnlock(&lang_mutex);
/*
- * '_cupsMessageLoad()' - Load a .po file into a messages array.
+ * '_cupsMessageLoad()' - Load a .po or .strings file into a messages array.
*/
cups_array_t * /* O - New message array */
_cupsMessageLoad(const char *filename, /* I - Message catalog to load */
- int unquote) /* I - Unescape \foo in strings? */
+ int flags) /* I - Load flags */
{
cups_file_t *fp; /* Message file */
cups_array_t *a; /* Message array */
char s[4096], /* String buffer */
*ptr, /* Pointer into buffer */
*temp; /* New string */
- int length; /* Length of combined strings */
+ size_t length, /* Length of combined strings */
+ ptrlen; /* Length of string */
DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename));
* Create an array to hold the messages...
*/
- if ((a = cupsArrayNew3((cups_array_func_t)cups_message_compare, NULL,
- (cups_ahash_func_t)NULL, 0,
- (cups_acopy_func_t)NULL,
- (cups_afree_func_t)cups_message_free)) == NULL)
+ if ((a = _cupsMessageNew(NULL)) == NULL)
{
DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
return (NULL);
return (a);
}
- /*
- * Read messages from the catalog file until EOF...
- *
- * The format is the GNU gettext .po format, which is fairly simple:
- *
- * msgid "some text"
- * msgstr "localized text"
- *
- * The ID and localized text can span multiple lines using the form:
- *
- * msgid ""
- * "some long text"
- * msgstr ""
- * "localized text spanning "
- * "multiple lines"
- */
-
- m = NULL;
-
- while (cupsFileGets(fp, s, sizeof(s)) != NULL)
+ if (flags & _CUPS_MESSAGE_STRINGS)
+ {
+ while (cups_read_strings(fp, flags, a));
+ }
+ else
{
/*
- * Skip blank and comment lines...
+ * Read messages from the catalog file until EOF...
+ *
+ * The format is the GNU gettext .po format, which is fairly simple:
+ *
+ * msgid "some text"
+ * msgstr "localized text"
+ *
+ * The ID and localized text can span multiple lines using the form:
+ *
+ * msgid ""
+ * "some long text"
+ * msgstr ""
+ * "localized text spanning "
+ * "multiple lines"
*/
- if (s[0] == '#' || !s[0])
- continue;
+ m = NULL;
- /*
- * Strip the trailing quote...
- */
+ while (cupsFileGets(fp, s, sizeof(s)) != NULL)
+ {
+ /*
+ * Skip blank and comment lines...
+ */
- if ((ptr = strrchr(s, '\"')) == NULL)
- continue;
+ if (s[0] == '#' || !s[0])
+ continue;
- *ptr = '\0';
+ /*
+ * Strip the trailing quote...
+ */
- /*
- * Find start of value...
- */
-
- if ((ptr = strchr(s, '\"')) == NULL)
- continue;
+ if ((ptr = strrchr(s, '\"')) == NULL)
+ continue;
- ptr ++;
+ *ptr = '\0';
- /*
- * Unquote the text...
- */
+ /*
+ * Find start of value...
+ */
- if (unquote)
- cups_unquote(ptr, ptr);
+ if ((ptr = strchr(s, '\"')) == NULL)
+ continue;
- /*
- * Create or add to a message...
- */
+ ptr ++;
- if (!strncmp(s, "msgid", 5))
- {
/*
- * Add previous message as needed...
+ * Unquote the text...
*/
- if (m)
- cupsArrayAdd(a, m);
+ if (flags & _CUPS_MESSAGE_UNQUOTE)
+ cups_unquote(ptr, ptr);
/*
- * Create a new message with the given msgid string...
+ * Create or add to a message...
*/
- if ((m = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
+ if (!strncmp(s, "msgid", 5))
{
- cupsFileClose(fp);
- return (a);
- }
+ /*
+ * Add previous message as needed...
+ */
- if ((m->id = strdup(ptr)) == NULL)
- {
- free(m);
- cupsFileClose(fp);
- return (a);
- }
- }
- else if (s[0] == '\"' && m)
- {
- /*
- * Append to current string...
- */
+ if (m)
+ {
+ if (m->str && m->str[0])
+ {
+ cupsArrayAdd(a, m);
+ }
+ else
+ {
+ /*
+ * Translation is empty, don't add it... (STR #4033)
+ */
+
+ free(m->msg);
+ if (m->str)
+ free(m->str);
+ free(m);
+ }
+ }
- length = (int)strlen(m->str ? m->str : m->id);
+ /*
+ * Create a new message with the given msgid string...
+ */
- if ((temp = realloc(m->str ? m->str : m->id,
- length + strlen(ptr) + 1)) == NULL)
- {
- cupsFileClose(fp);
- return (a);
- }
+ if ((m = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
+ break;
- if (m->str)
+ if ((m->msg = strdup(ptr)) == NULL)
+ {
+ free(m);
+ m = NULL;
+ break;
+ }
+ }
+ else if (s[0] == '\"' && m)
{
/*
- * Copy the new portion to the end of the msgstr string - safe
- * to use strcpy because the buffer is allocated to the correct
- * size...
+ * Append to current string...
*/
- m->str = temp;
+ length = strlen(m->str ? m->str : m->msg);
+ ptrlen = strlen(ptr);
+
+ if ((temp = realloc(m->str ? m->str : m->msg, length + ptrlen + 1)) == NULL)
+ {
+ if (m->str)
+ free(m->str);
+ free(m->msg);
+ free(m);
+ m = NULL;
+ break;
+ }
+
+ if (m->str)
+ {
+ /*
+ * Copy the new portion to the end of the msgstr string - safe
+ * 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 memcpy because the buffer is allocated to the correct
+ * size...
+ */
+
+ m->msg = temp;
+
+ memcpy(m->msg + length, ptr, ptrlen + 1);
+ }
}
- else
+ else if (!strncmp(s, "msgstr", 6) && m)
{
/*
- * Copy the new portion to the end of the msgid string - safe
- * to use strcpy because the buffer is allocated to the correct
- * size...
+ * Set the string...
*/
- m->id = temp;
-
- strcpy(m->id + length, ptr);
+ if ((m->str = strdup(ptr)) == NULL)
+ {
+ free(m->msg);
+ free(m);
+ m = NULL;
+ break;
+ }
}
}
- else if (!strncmp(s, "msgstr", 6) && m)
- {
- /*
- * Set the string...
- */
- if ((m->str = strdup(ptr)) == NULL)
+ /*
+ * Add the last message string to the array as needed...
+ */
+
+ if (m)
+ {
+ if (m->str && m->str[0])
{
- cupsFileClose(fp);
- return (a);
+ cupsArrayAdd(a, m);
+ }
+ else
+ {
+ /*
+ * Translation is empty, don't add it... (STR #4033)
+ */
+
+ free(m->msg);
+ if (m->str)
+ free(m->str);
+ free(m);
}
}
}
- /*
- * 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...
*/
cupsFileClose(fp);
- DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
- cupsArrayCount(a)));
+ DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...", cupsArrayCount(a)));
return (a);
}
*match; /* Matching message */
+ DEBUG_printf(("_cupsMessageLookup(a=%p, m=\"%s\")", (void *)a, m));
+
/*
* Lookup the message string; if it doesn't exist in the catalog,
* then return the message that was passed to us...
*/
- key.id = (char *)m;
- match = (_cups_message_t *)cupsArrayFind(a, &key);
+ key.msg = (char *)m;
+ match = (_cups_message_t *)cupsArrayFind(a, &key);
#if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
if (!match && cupsArrayUserData(a))
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;
+ dict = (CFDictionaryRef)cupsArrayUserData(a);
+ cfm = CFStringCreateWithCString(kCFAllocatorDefault, m, kCFStringEncodingUTF8);
+ match = calloc(1, sizeof(_cups_message_t));
+ match->msg = strdup(m);
+ cfstr = cfm ? CFDictionaryGetValue(dict, cfm) : NULL;
if (cfstr)
{
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)
}
+/*
+ * '_cupsMessageNew()' - Make a new message catalog array.
+ */
+
+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));
+}
+
+
+/*
+ * '_cupsMessageSave()' - Save a message catalog array.
+ */
+
+int /* O - 0 on success, -1 on failure */
+_cupsMessageSave(const char *filename,/* I - Output filename */
+ int flags, /* I - Format flags */
+ cups_array_t *a) /* I - Message array */
+{
+ cups_file_t *fp; /* Output file */
+ _cups_message_t *m; /* Current message */
+
+
+ /*
+ * Output message catalog file...
+ */
+
+ if ((fp = cupsFileOpen(filename, "w")) == NULL)
+ return (-1);
+
+ /*
+ * Write each message...
+ */
+
+ if (flags & _CUPS_MESSAGE_STRINGS)
+ {
+ for (m = (_cups_message_t *)cupsArrayFirst(a); m; m = (_cups_message_t *)cupsArrayNext(a))
+ {
+ cupsFilePuts(fp, "\"");
+ cups_message_puts(fp, m->msg);
+ cupsFilePuts(fp, "\" = \"");
+ cups_message_puts(fp, m->str);
+ cupsFilePuts(fp, "\";\n");
+ }
+ }
+ else
+ {
+ for (m = (_cups_message_t *)cupsArrayFirst(a); m; m = (_cups_message_t *)cupsArrayNext(a))
+ {
+ cupsFilePuts(fp, "msgid \"");
+ cups_message_puts(fp, m->msg);
+ cupsFilePuts(fp, "\"\nmsgstr \"");
+ cups_message_puts(fp, m->str);
+ cupsFilePuts(fp, "\"\n");
+ }
+ }
+
+ return (cupsFileClose(fp));
+}
+
+
#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;
+ CFPropertyListRef localizationList = NULL;
/* 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 */
{
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)
{
- localizationList =
- CFBundleCopyPreferredLocalizationsFromArray(bundleList);
+ CFURLRef resources = CFBundleCopyResourcesDirectoryURL(bundle);
+
+ DEBUG_puts("3appleLangDefault: Getting localizationList from bundle.");
+
+ if (resources)
+ {
+ CFStringRef cfpath = CFURLCopyPath(resources);
+ char path[1024];
+
+ if (cfpath)
+ {
+ /*
+ * See if we have an Info.plist file in the bundle...
+ */
+
+ CFStringGetCString(cfpath, path, sizeof(path), kCFStringEncodingUTF8);
+ DEBUG_printf(("3appleLangDefault: Got a resource URL (\"%s\")", path));
+ strlcat(path, "Contents/Info.plist", sizeof(path));
+
+ if (!access(path, R_OK))
+ localizationList = CFBundleCopyPreferredLocalizationsFromArray(bundleList);
+ else
+ DEBUG_puts("3appleLangDefault: No Info.plist, ignoring resource URL...");
+
+ CFRelease(cfpath);
+ }
+
+ CFRelease(resources);
+ }
+ else
+ DEBUG_puts("3appleLangDefault: No resource URL.");
CFRelease(bundleList);
}
- else
+
+ if (!localizationList)
+ {
+ DEBUG_puts("3appleLangDefault: Getting localizationList from preferences.");
+
localizationList =
CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
kCFPreferencesCurrentApplication);
+ }
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)
{
if (languageName &&
CFGetTypeID(languageName) == CFStringGetTypeID())
{
- localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(
- kCFAllocatorDefault, languageName);
-
- if (localeName)
- {
- CFStringGetCString(localeName, cg->language, sizeof(cg->language),
- kCFStringEncodingASCII);
- CFRelease(localeName);
-
- DEBUG_printf(("9appleLangDefault: cg->language=\"%s\"",
+ if (_cupsAppleLocale(languageName, cg->language, sizeof(cg->language)))
+ 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(("9appleLangDefault: 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 locale.");
}
}
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));
+ }
}
+ else
+ DEBUG_printf(("3appleLangDefault: Using previous locale \"%s\".", cg->language));
/*
* Return the cached locale...
# 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 */
-
/*
* 'appleMessageLoad()' - Load a message catalog from a localizable bundle.
*/
static cups_array_t * /* O - Message catalog */
appleMessageLoad(const char *locale) /* I - Locale ID */
-CF_RETURNS_RETAINED
{
char filename[1024], /* Path to cups.strings file */
- applelang[256]; /* Apple language ID */
+ applelang[256], /* Apple language ID */
+ baselang[4]; /* Base language */
CFURLRef url; /* URL to cups.strings file */
- CFReadStreamRef stream; /* File */
- CFDictionaryRef dict; /* Localization dictionary */
+ CFReadStreamRef stream = NULL; /* File stream */
+ CFPropertyListRef plist = NULL; /* Localization file */
+#ifdef DEBUG
+ const char *cups_strings = getenv("CUPS_STRINGS");
+ /* Test strings file */
+ CFErrorRef error = NULL; /* Error when opening file */
+#endif /* DEBUG */
+
+
+ DEBUG_printf(("appleMessageLoad(locale=\"%s\")", locale));
/*
* Load the cups.strings file...
*/
+#ifdef DEBUG
+ if (cups_strings)
+ {
+ DEBUG_puts("1appleMessageLoad: Using debug CUPS_STRINGS file.");
+ strlcpy(filename, cups_strings, sizeof(filename));
+ }
+ else
+#endif /* DEBUG */
+
snprintf(filename, sizeof(filename),
CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings",
_cupsAppleLanguage(locale, applelang, sizeof(applelang)));
- url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
- (UInt8 *)filename,
- strlen(filename), false);
- stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
- dict = (CFDictionaryRef)CFPropertyListCreateWithStream(
- kCFAllocatorDefault, stream, 0, kCFPropertyListImmutable, NULL,
- NULL);
- CFRelease(stream);
- CFRelease(url);
+ if (access(filename, 0))
+ {
+ /*
+ * <rdar://problem/22086642>
+ *
+ * Try with original locale string...
+ */
+
+ DEBUG_printf(("1appleMessageLoad: \"%s\": %s", filename, strerror(errno)));
+ snprintf(filename, sizeof(filename), CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", locale);
+ }
+
+ if (access(filename, 0))
+ {
+ /*
+ * <rdar://problem/25292403>
+ *
+ * Try with just the language code...
+ */
+
+ DEBUG_printf(("1appleMessageLoad: \"%s\": %s", filename, strerror(errno)));
+
+ strlcpy(baselang, locale, sizeof(baselang));
+ if (baselang[3] == '-' || baselang[3] == '_')
+ baselang[3] = '\0';
+
+ snprintf(filename, sizeof(filename), CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", baselang);
+ }
+
+ if (access(filename, 0))
+ {
+ /*
+ * Try alternate lproj directory names...
+ */
+
+ DEBUG_printf(("1appleMessageLoad: \"%s\": %s", filename, strerror(errno)));
+
+ if (!strncmp(locale, "en", 2))
+ locale = "English";
+ else if (!strncmp(locale, "nb", 2))
+ locale = "no";
+ else if (!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";
+ else if (!strcmp(locale, "zh_HK") || !strncasecmp(locale, "zh-Hant", 7) || !strncasecmp(locale, "zh_Hant", 7))
+ {
+ /*
+ * <rdar://problem/22130168>
+ * <rdar://problem/27245567>
+ *
+ * Try zh_TW first, then zh... Sigh...
+ */
+
+ if (!access(CUPS_BUNDLEDIR "/Resources/zh_TW.lproj/cups.strings", 0))
+ locale = "zh_TW";
+ else
+ locale = "zh";
+ }
+ else if (strstr(locale, "_") != NULL || strstr(locale, "-") != NULL)
+ {
+ /*
+ * Drop country code, just try language...
+ */
+
+ strlcpy(baselang, locale, sizeof(baselang));
+ if (baselang[2] == '-' || baselang[2] == '_')
+ baselang[2] = '\0';
+
+ locale = baselang;
+ }
+
+ snprintf(filename, sizeof(filename),
+ CUPS_BUNDLEDIR "/Resources/%s.lproj/cups.strings", locale);
+ }
+
+ DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename));
+
+ url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+ (UInt8 *)filename,
+ (CFIndex)strlen(filename), false);
+ if (url)
+ {
+ stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
+ if (stream)
+ {
+ /*
+ * 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)
+ {
+ CFStringRef msg = CFErrorCopyDescription(error);
+ /* Error message */
+
+ CFStringGetCString(msg, filename, sizeof(filename),
+ kCFStringEncodingUTF8);
+ DEBUG_printf(("1appleMessageLoad: %s", filename));
+
+ CFRelease(msg);
+ CFRelease(error);
+ }
+
+#else
+ plist = CFPropertyListCreateWithStream(kCFAllocatorDefault, stream, 0,
+ kCFPropertyListImmutable, NULL,
+ NULL);
+#endif /* DEBUG */
+
+ if (plist && CFGetTypeID(plist) != CFDictionaryGetTypeID())
+ {
+ CFRelease(plist);
+ plist = NULL;
+ }
+
+ CFRelease(stream);
+ }
+
+ CFRelease(url);
+ }
+
+ DEBUG_printf(("1appleMessageLoad: url=%p, stream=%p, plist=%p", url, stream,
+ plist));
/*
* Create and return an empty array to act as a cache for messages, passing the
- * dictionary as the user data.
+ * plist as the user data.
*/
- return (cupsArrayNew3((cups_array_func_t)cups_message_compare, (void *)dict,
- (cups_ahash_func_t)NULL, 0,
- (cups_acopy_func_t)NULL,
- (cups_afree_func_t)cups_message_free));
+ return (_cupsMessageNew((void *)plist));
}
# endif /* CUPS_BUNDLEDIR */
#endif /* __APPLE__ */
for (lang = lang_cache; lang != NULL; lang = lang->next)
{
DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
- "encoding=%d(%s)", lang, lang->language, lang->encoding,
+ "encoding=%d(%s)", (void *)lang, lang->language, lang->encoding,
lang_encodings[lang->encoding]));
if (!strcmp(lang->language, name) &&
_cups_message_t *m1, /* I - First message */
_cups_message_t *m2) /* I - Second message */
{
- return (strcmp(m1->id, m2->id));
+ return (strcmp(m1->msg, m2->msg));
}
static void
cups_message_free(_cups_message_t *m) /* I - Message */
{
- if (m->id)
- free(m->id);
+ if (m->msg)
+ free(m->msg);
if (m->str)
free(m->str);
}
+/*
+ * '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, _CUPS_MESSAGE_UNQUOTE);
+#endif /* __APPLE__ && CUPS_BUNDLEDIR */
+}
+
+
+/*
+ * 'cups_message_puts()' - Write a message string with quoting.
+ */
+
+static void
+cups_message_puts(cups_file_t *fp, /* I - File to write to */
+ const char *s) /* I - String to write */
+{
+ const char *start, /* Start of substring */
+ *ptr; /* Pointer into string */
+
+
+ for (start = s, ptr = s; *ptr; ptr ++)
+ {
+ if (strchr("\\\"\n\t", *ptr))
+ {
+ if (ptr > start)
+ {
+ cupsFileWrite(fp, start, (size_t)(ptr - start));
+ start = ptr + 1;
+ }
+
+ if (*ptr == '\\')
+ cupsFileWrite(fp, "\\\\", 2);
+ else if (*ptr == '\"')
+ cupsFileWrite(fp, "\\\"", 2);
+ else if (*ptr == '\n')
+ cupsFileWrite(fp, "\\n", 2);
+ else /* if (*ptr == '\t') */
+ cupsFileWrite(fp, "\\t", 2);
+ }
+ }
+
+ if (ptr > start)
+ cupsFileWrite(fp, start, (size_t)(ptr - start));
+}
+
+
+/*
+ * 'cups_read_strings()' - Read a pair of strings from a .strings file.
+ */
+
+static int /* O - 1 on success, 0 on failure */
+cups_read_strings(cups_file_t *fp, /* I - .strings file */
+ int flags, /* I - CUPS_MESSAGE_xxx flags */
+ cups_array_t *a) /* I - Message catalog array */
+{
+ char buffer[8192], /* Line buffer */
+ *bufptr, /* Pointer into buffer */
+ *msg, /* Pointer to start of message */
+ *str; /* Pointer to start of translation string */
+ _cups_message_t *m; /* New message */
+
+
+ while (cupsFileGets(fp, buffer, sizeof(buffer)))
+ {
+ /*
+ * Skip any line (comments, blanks, etc.) that isn't:
+ *
+ * "message" = "translation";
+ */
+
+ for (bufptr = buffer; *bufptr && isspace(*bufptr & 255); bufptr ++);
+
+ if (*bufptr != '\"')
+ continue;
+
+ /*
+ * Find the end of the message...
+ */
+
+ bufptr ++;
+ for (msg = bufptr; *bufptr && *bufptr != '\"'; bufptr ++)
+ if (*bufptr == '\\' && bufptr[1])
+ bufptr ++;
+
+ if (!*bufptr)
+ continue;
+
+ *bufptr++ = '\0';
+
+ if (flags & _CUPS_MESSAGE_UNQUOTE)
+ cups_unquote(msg, msg);
+
+ /*
+ * Find the start of the translation...
+ */
+
+ while (*bufptr && isspace(*bufptr & 255))
+ bufptr ++;
+
+ if (*bufptr != '=')
+ continue;
+
+ bufptr ++;
+ while (*bufptr && isspace(*bufptr & 255))
+ bufptr ++;
+
+ if (*bufptr != '\"')
+ continue;
+
+ /*
+ * Find the end of the translation...
+ */
+
+ bufptr ++;
+ for (str = bufptr; *bufptr && *bufptr != '\"'; bufptr ++)
+ if (*bufptr == '\\' && bufptr[1])
+ bufptr ++;
+
+ if (!*bufptr)
+ continue;
+
+ *bufptr++ = '\0';
+
+ if (flags & _CUPS_MESSAGE_UNQUOTE)
+ cups_unquote(str, str);
+
+ /*
+ * If we get this far we have a valid pair of strings, add them...
+ */
+
+ if ((m = malloc(sizeof(_cups_message_t))) == NULL)
+ break;
+
+ m->msg = strdup(msg);
+ m->str = strdup(str);
+
+ if (m->msg && m->str)
+ {
+ cupsArrayAdd(a, m);
+ }
+ else
+ {
+ if (m->msg)
+ free(m->msg);
+
+ if (m->str)
+ free(m->str);
+
+ free(m);
+ break;
+ }
+
+ return (1);
+ }
+
+ /*
+ * No more strings...
+ */
+
+ return (0);
+}
+
+
/*
* 'cups_unquote()' - Unquote characters in strings...
*/
*d = '\0';
}
-
-
-/*
- * End of "$Id: language.c 7558 2008-05-12 23:46:44Z mike $".
- */