4 * I18N/language support for CUPS.
6 * Copyright 2007-2015 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
15 * This file is subject to the Apple OS-Developed Software exception.
19 * Include necessary headers...
22 #include "cups-private.h"
23 #ifdef HAVE_LANGINFO_H
24 # include <langinfo.h>
25 #endif /* HAVE_LANGINFO_H */
31 #ifdef HAVE_COREFOUNDATION_H
32 # include <CoreFoundation/CoreFoundation.h>
33 #endif /* HAVE_COREFOUNDATION_H */
40 static _cups_mutex_t lang_mutex
= _CUPS_MUTEX_INITIALIZER
;
41 /* Mutex to control access to cache */
42 static cups_lang_t
*lang_cache
= NULL
;
43 /* Language string cache */
44 static const char * const lang_encodings
[] =
45 { /* Encoding strings */
46 "us-ascii", "iso-8859-1",
47 "iso-8859-2", "iso-8859-3",
48 "iso-8859-4", "iso-8859-5",
49 "iso-8859-6", "iso-8859-7",
50 "iso-8859-8", "iso-8859-9",
51 "iso-8859-10", "utf-8",
52 "iso-8859-13", "iso-8859-14",
53 "iso-8859-15", "cp874",
59 "koi8-u", "iso-8859-11",
100 "unknown", "unknown",
101 "unknown", "unknown",
102 "unknown", "unknown",
103 "unknown", "unknown",
104 "unknown", "unknown",
105 "unknown", "unknown",
106 "unknown", "unknown",
107 "unknown", "unknown",
108 "unknown", "unknown",
109 "unknown", "unknown",
118 const char * const language
; /* Language ID */
119 const char * const locale
; /* Locale ID */
120 } _apple_language_locale_t
;
122 static const _apple_language_locale_t apple_language_locale
[] =
123 { /* Locale to language ID LUT */
126 { "zh-Hans", "zh_CN" },
127 { "zh-Hant", "zh_TW" }
129 #endif /* __APPLE__ */
138 static const char *appleLangDefault(void);
139 # ifdef CUPS_BUNDLEDIR
140 # ifndef CF_RETURNS_RETAINED
141 # if __has_feature(attribute_cf_returns_retained)
142 # define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
144 # define CF_RETURNS_RETAINED
145 # endif /* __has_feature(attribute_cf_returns_retained) */
146 # endif /* !CF_RETURNED_RETAINED */
147 static cups_array_t
*appleMessageLoad(const char *locale
)
149 # endif /* CUPS_BUNDLEDIR */
150 #endif /* __APPLE__ */
151 static cups_lang_t
*cups_cache_lookup(const char *name
,
152 cups_encoding_t encoding
);
153 static int cups_message_compare(_cups_message_t
*m1
,
154 _cups_message_t
*m2
);
155 static void cups_message_free(_cups_message_t
*m
);
156 static void cups_message_load(cups_lang_t
*lang
);
157 static void cups_unquote(char *d
, const char *s
);
162 * '_cupsAppleLanguage()' - Get the Apple language identifier associated with a
166 const char * /* O - Language ID */
167 _cupsAppleLanguage(const char *locale
, /* I - Locale ID */
168 char *language
,/* I - Language ID buffer */
169 size_t langsize
) /* I - Size of language ID buffer */
171 int i
; /* Looping var */
172 CFStringRef localeid
, /* CF locale identifier */
173 langid
; /* CF language identifier */
177 * Copy the locale name and convert, as needed, to the Apple-specific
178 * locale identifier...
181 switch (strlen(locale
))
188 strlcpy(language
, "en", langsize
);
192 strlcpy(language
, locale
, langsize
);
196 strlcpy(language
, locale
, langsize
);
198 if (language
[2] == '-')
201 * Convert ll-cc to ll_CC...
205 language
[3] = (char)toupper(language
[3] & 255);
206 language
[4] = (char)toupper(language
[4] & 255);
212 i
< (int)(sizeof(apple_language_locale
) /
213 sizeof(apple_language_locale
[0]));
215 if (!strcmp(locale
, apple_language_locale
[i
].locale
))
217 strlcpy(language
, apple_language_locale
[i
].language
, sizeof(language
));
222 * Attempt to map the locale ID to a language ID...
225 if ((localeid
= CFStringCreateWithCString(kCFAllocatorDefault
, language
,
226 kCFStringEncodingASCII
)) != NULL
)
228 if ((langid
= CFLocaleCreateCanonicalLanguageIdentifierFromString(
229 kCFAllocatorDefault
, localeid
)) != NULL
)
231 CFStringGetCString(langid
, language
, (CFIndex
)langsize
, kCFStringEncodingASCII
);
239 * Return what we got...
244 #endif /* __APPLE__ */
248 * '_cupsEncodingName()' - Return the character encoding name string
249 * for the given encoding enumeration.
252 const char * /* O - Character encoding */
254 cups_encoding_t encoding
) /* I - Encoding value */
256 if (encoding
< CUPS_US_ASCII
||
257 encoding
>= (cups_encoding_t
)(sizeof(lang_encodings
) / sizeof(lang_encodings
[0])))
259 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = out of range (\"%s\")",
260 encoding
, lang_encodings
[0]));
261 return (lang_encodings
[0]);
265 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = \"%s\"",
266 encoding
, lang_encodings
[encoding
]));
267 return (lang_encodings
[encoding
]);
273 * 'cupsLangDefault()' - Return the default language.
276 cups_lang_t
* /* O - Language data */
277 cupsLangDefault(void)
279 return (cupsLangGet(NULL
));
284 * 'cupsLangEncoding()' - Return the character encoding (us-ascii, etc.)
285 * for the given language.
288 const char * /* O - Character encoding */
289 cupsLangEncoding(cups_lang_t
*lang
) /* I - Language data */
292 return ((char*)lang_encodings
[0]);
294 return ((char*)lang_encodings
[lang
->encoding
]);
299 * 'cupsLangFlush()' - Flush all language data out of the cache.
305 cups_lang_t
*lang
, /* Current language */
306 *next
; /* Next language */
310 * Free all languages in the cache...
313 _cupsMutexLock(&lang_mutex
);
315 for (lang
= lang_cache
; lang
!= NULL
; lang
= next
)
318 * Free all messages...
321 _cupsMessageFree(lang
->strings
);
324 * Then free the language structure itself...
333 _cupsMutexUnlock(&lang_mutex
);
338 * 'cupsLangFree()' - Free language data.
340 * This does not actually free anything; use @link cupsLangFlush@ for that.
344 cupsLangFree(cups_lang_t
*lang
) /* I - Language to free */
346 _cupsMutexLock(&lang_mutex
);
348 if (lang
!= NULL
&& lang
->used
> 0)
351 _cupsMutexUnlock(&lang_mutex
);
356 * 'cupsLangGet()' - Get a language.
359 cups_lang_t
* /* O - Language data */
360 cupsLangGet(const char *language
) /* I - Language or locale */
362 int i
; /* Looping var */
364 char locale
[255]; /* Copy of locale name */
365 #endif /* !__APPLE__ */
366 char langname
[16], /* Requested language name */
367 country
[16], /* Country code */
368 charset
[16], /* Character set */
369 *csptr
, /* Pointer to CODESET string */
370 *ptr
, /* Pointer into language/charset */
371 real
[48]; /* Real language name */
372 cups_encoding_t encoding
; /* Encoding to use */
373 cups_lang_t
*lang
; /* Current language... */
374 static const char * const locale_encodings
[] =
375 { /* Locale charset names */
376 "ASCII", "ISO88591", "ISO88592", "ISO88593",
377 "ISO88594", "ISO88595", "ISO88596", "ISO88597",
378 "ISO88598", "ISO88599", "ISO885910", "UTF8",
379 "ISO885913", "ISO885914", "ISO885915", "CP874",
380 "CP1250", "CP1251", "CP1252", "CP1253",
381 "CP1254", "CP1255", "CP1256", "CP1257",
382 "CP1258", "KOI8R", "KOI8U", "ISO885911",
383 "ISO885916", "MACROMAN", "", "",
394 "CP932", "CP936", "CP949", "CP950",
395 "CP1361", "", "", "",
412 "EUCCN", "EUCJP", "EUCKR", "EUCTW",
417 DEBUG_printf(("2cupsLangGet(language=\"%s\")", language
));
421 * Set the character set to UTF-8...
424 strlcpy(charset
, "UTF8", sizeof(charset
));
427 * Apple's setlocale doesn't give us the user's localization
428 * preference so we have to look it up this way...
433 if (!getenv("SOFTWARE") || (language
= getenv("LANG")) == NULL
)
434 language
= appleLangDefault();
436 DEBUG_printf(("4cupsLangGet: language=\"%s\"", language
));
441 * Set the charset to "unknown"...
447 * Use setlocale() to determine the currently set locale, and then
448 * fallback to environment variables to avoid setting the locale,
449 * since setlocale() is not thread-safe!
455 * First see if the locale has been set; if it is still "C" or
456 * "POSIX", use the environment to get the default...
460 ptr
= setlocale(LC_MESSAGES
, NULL
);
462 ptr
= setlocale(LC_ALL
, NULL
);
463 # endif /* LC_MESSAGES */
465 DEBUG_printf(("4cupsLangGet: current locale is \"%s\"", ptr
));
467 if (!ptr
|| !strcmp(ptr
, "C") || !strcmp(ptr
, "POSIX"))
470 * Get the character set from the LC_CTYPE locale setting...
473 if ((ptr
= getenv("LC_CTYPE")) == NULL
)
474 if ((ptr
= getenv("LC_ALL")) == NULL
)
475 if ((ptr
= getenv("LANG")) == NULL
)
478 if ((csptr
= strchr(ptr
, '.')) != NULL
)
481 * Extract the character set from the environment...
484 for (ptr
= charset
, csptr
++; *csptr
; csptr
++)
485 if (ptr
< (charset
+ sizeof(charset
) - 1) && _cups_isalnum(*csptr
))
492 * Get the locale for messages from the LC_MESSAGES locale setting...
495 if ((ptr
= getenv("LC_MESSAGES")) == NULL
)
496 if ((ptr
= getenv("LC_ALL")) == NULL
)
497 if ((ptr
= getenv("LANG")) == NULL
)
503 strlcpy(locale
, ptr
, sizeof(locale
));
507 * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
510 if (!strncmp(locale
, "nb", 2))
513 DEBUG_printf(("4cupsLangGet: new language value is \"%s\"", language
));
516 #endif /* __APPLE__ */
519 * If "language" is NULL at this point, then chances are we are using
520 * a language that is not installed for the base OS.
526 * Switch to the POSIX ("C") locale...
534 * On systems that support the nl_langinfo(CODESET) call, use
535 * this value as the character set...
538 if (!charset
[0] && (csptr
= nl_langinfo(CODESET
)) != NULL
)
541 * Copy all of the letters and numbers in the CODESET string...
544 for (ptr
= charset
; *csptr
; csptr
++)
545 if (_cups_isalnum(*csptr
) && ptr
< (charset
+ sizeof(charset
) - 1))
550 DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via "
551 "nl_langinfo(CODESET)...", charset
));
556 * If we don't have a character set by now, default to UTF-8...
560 strlcpy(charset
, "UTF8", sizeof(charset
));
563 * Parse the language string passed in to a locale string. "C" is the
564 * standard POSIX locale and is copied unchanged. Otherwise the
565 * language string is converted from ll-cc[.charset] (language-country)
566 * to ll_CC[.CHARSET] to match the file naming convention used by all
567 * POSIX-compliant operating systems. Invalid language names are mapped
568 * to the POSIX locale.
573 if (language
== NULL
|| !language
[0] ||
574 !strcmp(language
, "POSIX"))
575 strlcpy(langname
, "C", sizeof(langname
));
579 * Copy the parts of the locale string over safely...
582 for (ptr
= langname
; *language
; language
++)
583 if (*language
== '_' || *language
== '-' || *language
== '.')
585 else if (ptr
< (langname
+ sizeof(langname
) - 1))
586 *ptr
++ = (char)tolower(*language
& 255);
590 if (*language
== '_' || *language
== '-')
593 * Copy the country code...
596 for (language
++, ptr
= country
; *language
; language
++)
597 if (*language
== '.')
599 else if (ptr
< (country
+ sizeof(country
) - 1))
600 *ptr
++ = (char)toupper(*language
& 255);
605 if (*language
== '.' && !charset
[0])
608 * Copy the encoding...
611 for (language
++, ptr
= charset
; *language
; language
++)
612 if (_cups_isalnum(*language
) && ptr
< (charset
+ sizeof(charset
) - 1))
613 *ptr
++ = (char)toupper(*language
& 255);
619 * Force a POSIX locale for an invalid language name...
622 if (strlen(langname
) != 2)
624 strlcpy(langname
, "C", sizeof(langname
));
630 DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"",
631 langname
, country
, charset
));
634 * Figure out the desired encoding...
637 encoding
= CUPS_AUTO_ENCODING
;
642 i
< (int)(sizeof(locale_encodings
) / sizeof(locale_encodings
[0]));
644 if (!_cups_strcasecmp(charset
, locale_encodings
[i
]))
646 encoding
= (cups_encoding_t
)i
;
650 if (encoding
== CUPS_AUTO_ENCODING
)
653 * Map alternate names for various character sets...
656 if (!_cups_strcasecmp(charset
, "iso-2022-jp") ||
657 !_cups_strcasecmp(charset
, "sjis"))
658 encoding
= CUPS_WINDOWS_932
;
659 else if (!_cups_strcasecmp(charset
, "iso-2022-cn"))
660 encoding
= CUPS_WINDOWS_936
;
661 else if (!_cups_strcasecmp(charset
, "iso-2022-kr"))
662 encoding
= CUPS_WINDOWS_949
;
663 else if (!_cups_strcasecmp(charset
, "big5"))
664 encoding
= CUPS_WINDOWS_950
;
668 DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding
,
669 encoding
== CUPS_AUTO_ENCODING
? "auto" :
670 lang_encodings
[encoding
]));
673 * See if we already have this language/country loaded...
677 snprintf(real
, sizeof(real
), "%s_%s", langname
, country
);
679 strlcpy(real
, langname
, sizeof(real
));
681 _cupsMutexLock(&lang_mutex
);
683 if ((lang
= cups_cache_lookup(real
, encoding
)) != NULL
)
685 _cupsMutexUnlock(&lang_mutex
);
687 DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real
));
693 * See if there is a free language available; if so, use that
697 for (lang
= lang_cache
; lang
!= NULL
; lang
= lang
->next
)
704 * Allocate memory for the language and add it to the cache.
707 if ((lang
= calloc(sizeof(cups_lang_t
), 1)) == NULL
)
709 _cupsMutexUnlock(&lang_mutex
);
714 lang
->next
= lang_cache
;
720 * Free all old strings as needed...
723 _cupsMessageFree(lang
->strings
);
724 lang
->strings
= NULL
;
728 * Then assign the language and encoding fields...
732 strlcpy(lang
->language
, real
, sizeof(lang
->language
));
734 if (encoding
!= CUPS_AUTO_ENCODING
)
735 lang
->encoding
= encoding
;
737 lang
->encoding
= CUPS_UTF8
;
743 _cupsMutexUnlock(&lang_mutex
);
750 * '_cupsLangString()' - Get a message string.
752 * The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to
753 * convert the string to the language encoding.
756 const char * /* O - Localized message */
757 _cupsLangString(cups_lang_t
*lang
, /* I - Language */
758 const char *message
) /* I - Message */
760 const char *s
; /* Localized message */
763 * Range check input...
766 if (!lang
|| !message
|| !*message
)
769 _cupsMutexLock(&lang_mutex
);
772 * Load the message catalog if needed...
776 cups_message_load(lang
);
778 s
= _cupsMessageLookup(lang
->strings
, message
);
780 _cupsMutexUnlock(&lang_mutex
);
787 * '_cupsMessageFree()' - Free a messages array.
791 _cupsMessageFree(cups_array_t
*a
) /* I - Message array */
793 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
795 * Release the cups.strings dictionary as needed...
798 if (cupsArrayUserData(a
))
799 CFRelease((CFDictionaryRef
)cupsArrayUserData(a
));
800 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
811 * '_cupsMessageLoad()' - Load a .po file into a messages array.
814 cups_array_t
* /* O - New message array */
815 _cupsMessageLoad(const char *filename
, /* I - Message catalog to load */
816 int unquote
) /* I - Unescape \foo in strings? */
818 cups_file_t
*fp
; /* Message file */
819 cups_array_t
*a
; /* Message array */
820 _cups_message_t
*m
; /* Current message */
821 char s
[4096], /* String buffer */
822 *ptr
, /* Pointer into buffer */
823 *temp
; /* New string */
824 size_t length
, /* Length of combined strings */
825 ptrlen
; /* Length of string */
828 DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename
));
831 * Create an array to hold the messages...
834 if ((a
= _cupsMessageNew(NULL
)) == NULL
)
836 DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
841 * Open the message catalog file...
844 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
846 DEBUG_printf(("5_cupsMessageLoad: Unable to open file: %s",
852 * Read messages from the catalog file until EOF...
854 * The format is the GNU gettext .po format, which is fairly simple:
857 * msgstr "localized text"
859 * The ID and localized text can span multiple lines using the form:
864 * "localized text spanning "
870 while (cupsFileGets(fp
, s
, sizeof(s
)) != NULL
)
873 * Skip blank and comment lines...
876 if (s
[0] == '#' || !s
[0])
880 * Strip the trailing quote...
883 if ((ptr
= strrchr(s
, '\"')) == NULL
)
889 * Find start of value...
892 if ((ptr
= strchr(s
, '\"')) == NULL
)
898 * Unquote the text...
902 cups_unquote(ptr
, ptr
);
905 * Create or add to a message...
908 if (!strncmp(s
, "msgid", 5))
911 * Add previous message as needed...
916 if (m
->str
&& m
->str
[0])
923 * Translation is empty, don't add it... (STR #4033)
934 * Create a new message with the given msgid string...
937 if ((m
= (_cups_message_t
*)calloc(1, sizeof(_cups_message_t
))) == NULL
)
943 if ((m
->id
= strdup(ptr
)) == NULL
)
950 else if (s
[0] == '\"' && m
)
953 * Append to current string...
956 length
= strlen(m
->str
? m
->str
: m
->id
);
957 ptrlen
= strlen(ptr
);
959 if ((temp
= realloc(m
->str
? m
->str
: m
->id
, length
+ ptrlen
+ 1)) == NULL
)
973 * Copy the new portion to the end of the msgstr string - safe
974 * to use memcpy because the buffer is allocated to the correct
980 memcpy(m
->str
+ length
, ptr
, ptrlen
+ 1);
985 * Copy the new portion to the end of the msgid string - safe
986 * to use memcpy because the buffer is allocated to the correct
992 memcpy(m
->id
+ length
, ptr
, ptrlen
+ 1);
995 else if (!strncmp(s
, "msgstr", 6) && m
)
1001 if ((m
->str
= strdup(ptr
)) == NULL
)
1013 * Add the last message string to the array as needed...
1018 if (m
->str
&& m
->str
[0])
1025 * Translation is empty, don't add it... (STR #4033)
1036 * Close the message catalog file and return the new array...
1041 DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
1042 cupsArrayCount(a
)));
1049 * '_cupsMessageLookup()' - Lookup a message string.
1052 const char * /* O - Localized message */
1053 _cupsMessageLookup(cups_array_t
*a
, /* I - Message array */
1054 const char *m
) /* I - Message */
1056 _cups_message_t key
, /* Search key */
1057 *match
; /* Matching message */
1061 * Lookup the message string; if it doesn't exist in the catalog,
1062 * then return the message that was passed to us...
1066 match
= (_cups_message_t
*)cupsArrayFind(a
, &key
);
1068 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1069 if (!match
&& cupsArrayUserData(a
))
1072 * Try looking the string up in the cups.strings dictionary...
1075 CFDictionaryRef dict
; /* cups.strings dictionary */
1076 CFStringRef cfm
, /* Message as a CF string */
1077 cfstr
; /* Localized text as a CF string */
1079 dict
= (CFDictionaryRef
)cupsArrayUserData(a
);
1080 cfm
= CFStringCreateWithCString(kCFAllocatorDefault
, m
,
1081 kCFStringEncodingUTF8
);
1082 match
= calloc(1, sizeof(_cups_message_t
));
1083 match
->id
= strdup(m
);
1084 cfstr
= cfm
? CFDictionaryGetValue(dict
, cfm
) : NULL
;
1088 char buffer
[1024]; /* Message buffer */
1090 CFStringGetCString(cfstr
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
);
1091 match
->str
= strdup(buffer
);
1093 DEBUG_printf(("1_cupsMessageLookup: Found \"%s\" as \"%s\"...",
1098 match
->str
= strdup(m
);
1100 DEBUG_printf(("1_cupsMessageLookup: Did not find \"%s\"...", m
));
1103 cupsArrayAdd(a
, match
);
1108 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
1110 if (match
&& match
->str
)
1111 return (match
->str
);
1118 * '_cupsMessageNew()' - Make a new message catalog array.
1121 cups_array_t
* /* O - Array */
1122 _cupsMessageNew(void *context
) /* I - User data */
1124 return (cupsArrayNew3((cups_array_func_t
)cups_message_compare
, context
,
1125 (cups_ahash_func_t
)NULL
, 0,
1126 (cups_acopy_func_t
)NULL
,
1127 (cups_afree_func_t
)cups_message_free
));
1133 * 'appleLangDefault()' - Get the default locale string.
1136 static const char * /* O - Locale string */
1137 appleLangDefault(void)
1139 int i
; /* Looping var */
1140 CFBundleRef bundle
; /* Main bundle (if any) */
1141 CFArrayRef bundleList
; /* List of localizations in bundle */
1142 CFPropertyListRef localizationList
= NULL
;
1143 /* List of localization data */
1144 CFStringRef languageName
; /* Current name */
1145 CFStringRef localeName
; /* Canonical from of name */
1146 char *lang
; /* LANG environment variable */
1147 _cups_globals_t
*cg
= _cupsGlobals();
1148 /* Pointer to library globals */
1151 DEBUG_puts("2appleLangDefault()");
1154 * Only do the lookup and translation the first time.
1157 if (!cg
->language
[0])
1159 if (getenv("SOFTWARE") != NULL
&& (lang
= getenv("LANG")) != NULL
)
1161 DEBUG_printf(("3appleLangDefault: Using LANG=%s", lang
));
1162 strlcpy(cg
->language
, lang
, sizeof(cg
->language
));
1163 return (cg
->language
);
1165 else if ((bundle
= CFBundleGetMainBundle()) != NULL
&&
1166 (bundleList
= CFBundleCopyBundleLocalizations(bundle
)) != NULL
)
1168 CFURLRef resources
= CFBundleCopyResourcesDirectoryURL(bundle
);
1170 DEBUG_puts("3appleLangDefault: Getting localizationList from bundle.");
1174 CFStringRef cfpath
= CFURLCopyPath(resources
);
1180 * See if we have an Info.plist file in the bundle...
1183 CFStringGetCString(cfpath
, path
, sizeof(path
), kCFStringEncodingUTF8
);
1184 DEBUG_printf(("3appleLangDefault: Got a resource URL (\"%s\")", path
));
1185 strlcat(path
, "Contents/Info.plist", sizeof(path
));
1187 if (!access(path
, R_OK
))
1188 localizationList
= CFBundleCopyPreferredLocalizationsFromArray(bundleList
);
1190 DEBUG_puts("3appleLangDefault: No Info.plist, ignoring resource URL...");
1195 CFRelease(resources
);
1198 DEBUG_puts("3appleLangDefault: No resource URL.");
1200 CFRelease(bundleList
);
1203 if (!localizationList
)
1205 DEBUG_puts("3appleLangDefault: Getting localizationList from preferences.");
1208 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1209 kCFPreferencesCurrentApplication
);
1212 if (localizationList
)
1215 if (CFGetTypeID(localizationList
) == CFArrayGetTypeID())
1216 DEBUG_printf(("3appleLangDefault: Got localizationList, %d entries.",
1217 (int)CFArrayGetCount(localizationList
)));
1219 DEBUG_puts("3appleLangDefault: Got localizationList but not an array.");
1222 if (CFGetTypeID(localizationList
) == CFArrayGetTypeID() &&
1223 CFArrayGetCount(localizationList
) > 0)
1225 languageName
= CFArrayGetValueAtIndex(localizationList
, 0);
1228 CFGetTypeID(languageName
) == CFStringGetTypeID())
1230 localeName
= CFLocaleCreateCanonicalLocaleIdentifierFromString(
1231 kCFAllocatorDefault
, languageName
);
1235 CFStringGetCString(localeName
, cg
->language
, sizeof(cg
->language
),
1236 kCFStringEncodingASCII
);
1237 CFRelease(localeName
);
1239 DEBUG_printf(("3appleLangDefault: cg->language=\"%s\"",
1243 * Map new language identifiers to locales...
1247 i
< (int)(sizeof(apple_language_locale
) /
1248 sizeof(apple_language_locale
[0]));
1251 if (!strcmp(cg
->language
, apple_language_locale
[i
].language
))
1253 DEBUG_printf(("3appleLangDefault: mapping \"%s\" to \"%s\"...",
1254 cg
->language
, apple_language_locale
[i
].locale
));
1255 strlcpy(cg
->language
, apple_language_locale
[i
].locale
,
1256 sizeof(cg
->language
));
1262 * Convert language subtag into region subtag...
1265 if (cg
->language
[2] == '-')
1266 cg
->language
[2] = '_';
1268 if (!strchr(cg
->language
, '.'))
1269 strlcat(cg
->language
, ".UTF-8", sizeof(cg
->language
));
1272 DEBUG_puts("3appleLangDefault: Unable to get localeName.");
1276 CFRelease(localizationList
);
1280 * If we didn't find the language, default to en_US...
1283 if (!cg
->language
[0])
1285 DEBUG_puts("3appleLangDefault: Defaulting to en_US.");
1286 strlcpy(cg
->language
, "en_US.UTF-8", sizeof(cg
->language
));
1290 DEBUG_printf(("3appleLangDefault: Using previous locale \"%s\".", cg
->language
));
1293 * Return the cached locale...
1296 return (cg
->language
);
1300 # ifdef CUPS_BUNDLEDIR
1302 * 'appleMessageLoad()' - Load a message catalog from a localizable bundle.
1305 static cups_array_t
* /* O - Message catalog */
1306 appleMessageLoad(const char *locale
) /* I - Locale ID */
1308 char filename
[1024], /* Path to cups.strings file */
1309 applelang
[256], /* Apple language ID */
1310 baselang
[3]; /* Base language */
1311 CFURLRef url
; /* URL to cups.strings file */
1312 CFReadStreamRef stream
= NULL
; /* File stream */
1313 CFPropertyListRef plist
= NULL
; /* Localization file */
1315 CFErrorRef error
= NULL
; /* Error when opening file */
1319 DEBUG_printf(("appleMessageLoad(locale=\"%s\")", locale
));
1322 * Load the cups.strings file...
1325 snprintf(filename
, sizeof(filename
),
1326 CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings",
1327 _cupsAppleLanguage(locale
, applelang
, sizeof(applelang
)));
1329 if (access(filename
, 0))
1332 * <rdar://problem/22086642>
1334 * Try with original locale string...
1337 snprintf(filename
, sizeof(filename
), CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings", locale
);
1340 DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename
));
1342 if (access(filename
, 0))
1345 * Try alternate lproj directory names...
1348 if (!strncmp(locale
, "en", 2))
1350 else if (!strncmp(locale
, "nb", 2) || !strncmp(locale
, "nl", 2))
1352 else if (!strncmp(locale
, "fr", 2))
1354 else if (!strncmp(locale
, "de", 2))
1356 else if (!strncmp(locale
, "it", 2))
1358 else if (!strncmp(locale
, "ja", 2))
1359 locale
= "Japanese";
1360 else if (!strncmp(locale
, "es", 2))
1362 else if (!strcmp(locale
, "zh_HK"))
1365 * <rdar://problem/22130168>
1367 * Try zh_TW first, then zh... Sigh...
1370 if (!access(CUPS_BUNDLEDIR
"/Resources/zh_TW.lproj/cups.strings", 0))
1375 else if (strstr(locale
, "_") != NULL
|| strstr(locale
, "-") != NULL
)
1378 * Drop country code, just try language...
1381 strlcpy(baselang
, locale
, sizeof(baselang
));
1385 snprintf(filename
, sizeof(filename
),
1386 CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings", locale
);
1387 DEBUG_printf(("1appleMessageLoad: alternate filename=\"%s\"", filename
));
1390 url
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
,
1392 (CFIndex
)strlen(filename
), false);
1395 stream
= CFReadStreamCreateWithFile(kCFAllocatorDefault
, url
);
1399 * Read the property list containing the localization data.
1401 * NOTE: This code currently generates a clang "potential leak"
1402 * warning, but the object is released in _cupsMessageFree().
1405 CFReadStreamOpen(stream
);
1408 plist
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, stream
, 0,
1409 kCFPropertyListImmutable
, NULL
,
1413 CFStringRef msg
= CFErrorCopyDescription(error
);
1416 CFStringGetCString(msg
, filename
, sizeof(filename
),
1417 kCFStringEncodingUTF8
);
1418 DEBUG_printf(("1appleMessageLoad: %s", filename
));
1425 plist
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, stream
, 0,
1426 kCFPropertyListImmutable
, NULL
,
1430 if (plist
&& CFGetTypeID(plist
) != CFDictionaryGetTypeID())
1442 DEBUG_printf(("1appleMessageLoad: url=%p, stream=%p, plist=%p", url
, stream
,
1446 * Create and return an empty array to act as a cache for messages, passing the
1447 * plist as the user data.
1450 return (_cupsMessageNew((void *)plist
));
1452 # endif /* CUPS_BUNDLEDIR */
1453 #endif /* __APPLE__ */
1457 * 'cups_cache_lookup()' - Lookup a language in the cache...
1460 static cups_lang_t
* /* O - Language data or NULL */
1462 const char *name
, /* I - Name of locale */
1463 cups_encoding_t encoding
) /* I - Encoding of locale */
1465 cups_lang_t
*lang
; /* Current language */
1468 DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name
,
1469 encoding
, encoding
== CUPS_AUTO_ENCODING
? "auto" :
1470 lang_encodings
[encoding
]));
1473 * Loop through the cache and return a match if found...
1476 for (lang
= lang_cache
; lang
!= NULL
; lang
= lang
->next
)
1478 DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
1479 "encoding=%d(%s)", lang
, lang
->language
, lang
->encoding
,
1480 lang_encodings
[lang
->encoding
]));
1482 if (!strcmp(lang
->language
, name
) &&
1483 (encoding
== CUPS_AUTO_ENCODING
|| encoding
== lang
->encoding
))
1487 DEBUG_puts("8cups_cache_lookup: returning match!");
1493 DEBUG_puts("8cups_cache_lookup: returning NULL!");
1500 * 'cups_message_compare()' - Compare two messages.
1503 static int /* O - Result of comparison */
1504 cups_message_compare(
1505 _cups_message_t
*m1
, /* I - First message */
1506 _cups_message_t
*m2
) /* I - Second message */
1508 return (strcmp(m1
->id
, m2
->id
));
1513 * 'cups_message_free()' - Free a message.
1517 cups_message_free(_cups_message_t
*m
) /* I - Message */
1530 * 'cups_message_load()' - Load the message catalog for a language.
1534 cups_message_load(cups_lang_t
*lang
) /* I - Language */
1536 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1537 lang
->strings
= appleMessageLoad(lang
->language
);
1540 char filename
[1024]; /* Filename for language locale file */
1541 _cups_globals_t
*cg
= _cupsGlobals();
1542 /* Pointer to library globals */
1545 snprintf(filename
, sizeof(filename
), "%s/%s/cups_%s.po", cg
->localedir
,
1546 lang
->language
, lang
->language
);
1548 if (strchr(lang
->language
, '_') && access(filename
, 0))
1551 * Country localization not available, look for generic localization...
1554 snprintf(filename
, sizeof(filename
), "%s/%.2s/cups_%.2s.po", cg
->localedir
,
1555 lang
->language
, lang
->language
);
1557 if (access(filename
, 0))
1560 * No generic localization, so use POSIX...
1563 DEBUG_printf(("4cups_message_load: access(\"%s\", 0): %s", filename
,
1566 snprintf(filename
, sizeof(filename
), "%s/C/cups_C.po", cg
->localedir
);
1571 * Read the strings from the file...
1574 lang
->strings
= _cupsMessageLoad(filename
, 1);
1575 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
1580 * 'cups_unquote()' - Unquote characters in strings...
1584 cups_unquote(char *d
, /* O - Unquoted string */
1585 const char *s
) /* I - Original string */
1598 *d
= *d
* 8 + *s
- '0';