2 * I18N/language support for CUPS.
4 * Copyright 2007-2016 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products.
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
11 * missing or damaged, see the license at "http://www.cups.org/".
13 * This file is subject to the Apple OS-Developed Software exception.
17 * Include necessary headers...
20 #include "cups-private.h"
21 #ifdef HAVE_LANGINFO_H
22 # include <langinfo.h>
23 #endif /* HAVE_LANGINFO_H */
29 #ifdef HAVE_COREFOUNDATION_H
30 # include <CoreFoundation/CoreFoundation.h>
31 #endif /* HAVE_COREFOUNDATION_H */
38 static _cups_mutex_t lang_mutex
= _CUPS_MUTEX_INITIALIZER
;
39 /* Mutex to control access to cache */
40 static cups_lang_t
*lang_cache
= NULL
;
41 /* Language string cache */
42 static const char * const lang_encodings
[] =
43 { /* Encoding strings */
44 "us-ascii", "iso-8859-1",
45 "iso-8859-2", "iso-8859-3",
46 "iso-8859-4", "iso-8859-5",
47 "iso-8859-6", "iso-8859-7",
48 "iso-8859-8", "iso-8859-9",
49 "iso-8859-10", "utf-8",
50 "iso-8859-13", "iso-8859-14",
51 "iso-8859-15", "cp874",
57 "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",
116 const char * const language
; /* Language ID */
117 const char * const locale
; /* Locale ID */
118 } _apple_language_locale_t
;
120 static const _apple_language_locale_t apple_language_locale
[] =
121 { /* Locale to language ID LUT */
124 { "zh-Hans", "zh_CN" },
125 { "zh-Hant", "zh_TW" }
127 #endif /* __APPLE__ */
136 static const char *appleLangDefault(void);
137 # ifdef CUPS_BUNDLEDIR
138 # ifndef CF_RETURNS_RETAINED
139 # if __has_feature(attribute_cf_returns_retained)
140 # define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
142 # define CF_RETURNS_RETAINED
143 # endif /* __has_feature(attribute_cf_returns_retained) */
144 # endif /* !CF_RETURNED_RETAINED */
145 static cups_array_t
*appleMessageLoad(const char *locale
)
147 # endif /* CUPS_BUNDLEDIR */
148 #endif /* __APPLE__ */
149 static cups_lang_t
*cups_cache_lookup(const char *name
,
150 cups_encoding_t encoding
);
151 static int cups_message_compare(_cups_message_t
*m1
,
152 _cups_message_t
*m2
);
153 static void cups_message_free(_cups_message_t
*m
);
154 static void cups_message_load(cups_lang_t
*lang
);
155 static void cups_unquote(char *d
, const char *s
);
160 * '_cupsAppleLanguage()' - Get the Apple language identifier associated with a
164 const char * /* O - Language ID */
165 _cupsAppleLanguage(const char *locale
, /* I - Locale ID */
166 char *language
,/* I - Language ID buffer */
167 size_t langsize
) /* I - Size of language ID buffer */
169 int i
; /* Looping var */
170 CFStringRef localeid
, /* CF locale identifier */
171 langid
; /* CF language identifier */
175 * Copy the locale name and convert, as needed, to the Apple-specific
176 * locale identifier...
179 switch (strlen(locale
))
186 strlcpy(language
, "en", langsize
);
190 strlcpy(language
, locale
, langsize
);
194 strlcpy(language
, locale
, langsize
);
196 if (language
[2] == '-')
199 * Convert ll-cc to ll_CC...
203 language
[3] = (char)toupper(language
[3] & 255);
204 language
[4] = (char)toupper(language
[4] & 255);
210 i
< (int)(sizeof(apple_language_locale
) /
211 sizeof(apple_language_locale
[0]));
213 if (!strcmp(locale
, apple_language_locale
[i
].locale
))
215 strlcpy(language
, apple_language_locale
[i
].language
, sizeof(language
));
220 * Attempt to map the locale ID to a language ID...
223 if ((localeid
= CFStringCreateWithCString(kCFAllocatorDefault
, language
,
224 kCFStringEncodingASCII
)) != NULL
)
226 if ((langid
= CFLocaleCreateCanonicalLanguageIdentifierFromString(
227 kCFAllocatorDefault
, localeid
)) != NULL
)
229 CFStringGetCString(langid
, language
, (CFIndex
)langsize
, kCFStringEncodingASCII
);
237 * Return what we got...
242 #endif /* __APPLE__ */
246 * '_cupsEncodingName()' - Return the character encoding name string
247 * for the given encoding enumeration.
250 const char * /* O - Character encoding */
252 cups_encoding_t encoding
) /* I - Encoding value */
254 if (encoding
< CUPS_US_ASCII
||
255 encoding
>= (cups_encoding_t
)(sizeof(lang_encodings
) / sizeof(lang_encodings
[0])))
257 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = out of range (\"%s\")",
258 encoding
, lang_encodings
[0]));
259 return (lang_encodings
[0]);
263 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = \"%s\"",
264 encoding
, lang_encodings
[encoding
]));
265 return (lang_encodings
[encoding
]);
271 * 'cupsLangDefault()' - Return the default language.
274 cups_lang_t
* /* O - Language data */
275 cupsLangDefault(void)
277 return (cupsLangGet(NULL
));
282 * 'cupsLangEncoding()' - Return the character encoding (us-ascii, etc.)
283 * for the given language.
286 const char * /* O - Character encoding */
287 cupsLangEncoding(cups_lang_t
*lang
) /* I - Language data */
290 return ((char*)lang_encodings
[0]);
292 return ((char*)lang_encodings
[lang
->encoding
]);
297 * 'cupsLangFlush()' - Flush all language data out of the cache.
303 cups_lang_t
*lang
, /* Current language */
304 *next
; /* Next language */
308 * Free all languages in the cache...
311 _cupsMutexLock(&lang_mutex
);
313 for (lang
= lang_cache
; lang
!= NULL
; lang
= next
)
316 * Free all messages...
319 _cupsMessageFree(lang
->strings
);
322 * Then free the language structure itself...
331 _cupsMutexUnlock(&lang_mutex
);
336 * 'cupsLangFree()' - Free language data.
338 * This does not actually free anything; use @link cupsLangFlush@ for that.
342 cupsLangFree(cups_lang_t
*lang
) /* I - Language to free */
344 _cupsMutexLock(&lang_mutex
);
346 if (lang
!= NULL
&& lang
->used
> 0)
349 _cupsMutexUnlock(&lang_mutex
);
354 * 'cupsLangGet()' - Get a language.
357 cups_lang_t
* /* O - Language data */
358 cupsLangGet(const char *language
) /* I - Language or locale */
360 int i
; /* Looping var */
362 char locale
[255]; /* Copy of locale name */
363 #endif /* !__APPLE__ */
364 char langname
[16], /* Requested language name */
365 country
[16], /* Country code */
366 charset
[16], /* Character set */
367 *csptr
, /* Pointer to CODESET string */
368 *ptr
, /* Pointer into language/charset */
369 real
[48]; /* Real language name */
370 cups_encoding_t encoding
; /* Encoding to use */
371 cups_lang_t
*lang
; /* Current language... */
372 static const char * const locale_encodings
[] =
373 { /* Locale charset names */
374 "ASCII", "ISO88591", "ISO88592", "ISO88593",
375 "ISO88594", "ISO88595", "ISO88596", "ISO88597",
376 "ISO88598", "ISO88599", "ISO885910", "UTF8",
377 "ISO885913", "ISO885914", "ISO885915", "CP874",
378 "CP1250", "CP1251", "CP1252", "CP1253",
379 "CP1254", "CP1255", "CP1256", "CP1257",
380 "CP1258", "KOI8R", "KOI8U", "ISO885911",
381 "ISO885916", "MACROMAN", "", "",
392 "CP932", "CP936", "CP949", "CP950",
393 "CP1361", "", "", "",
410 "EUCCN", "EUCJP", "EUCKR", "EUCTW",
415 DEBUG_printf(("2cupsLangGet(language=\"%s\")", language
));
419 * Set the character set to UTF-8...
422 strlcpy(charset
, "UTF8", sizeof(charset
));
425 * Apple's setlocale doesn't give us the user's localization
426 * preference so we have to look it up this way...
431 if (!getenv("SOFTWARE") || (language
= getenv("LANG")) == NULL
)
432 language
= appleLangDefault();
434 DEBUG_printf(("4cupsLangGet: language=\"%s\"", language
));
439 * Set the charset to "unknown"...
445 * Use setlocale() to determine the currently set locale, and then
446 * fallback to environment variables to avoid setting the locale,
447 * since setlocale() is not thread-safe!
453 * First see if the locale has been set; if it is still "C" or
454 * "POSIX", use the environment to get the default...
458 ptr
= setlocale(LC_MESSAGES
, NULL
);
460 ptr
= setlocale(LC_ALL
, NULL
);
461 # endif /* LC_MESSAGES */
463 DEBUG_printf(("4cupsLangGet: current locale is \"%s\"", ptr
));
465 if (!ptr
|| !strcmp(ptr
, "C") || !strcmp(ptr
, "POSIX"))
468 * Get the character set from the LC_CTYPE locale setting...
471 if ((ptr
= getenv("LC_CTYPE")) == NULL
)
472 if ((ptr
= getenv("LC_ALL")) == NULL
)
473 if ((ptr
= getenv("LANG")) == NULL
)
476 if ((csptr
= strchr(ptr
, '.')) != NULL
)
479 * Extract the character set from the environment...
482 for (ptr
= charset
, csptr
++; *csptr
; csptr
++)
483 if (ptr
< (charset
+ sizeof(charset
) - 1) && _cups_isalnum(*csptr
))
490 * Get the locale for messages from the LC_MESSAGES locale setting...
493 if ((ptr
= getenv("LC_MESSAGES")) == NULL
)
494 if ((ptr
= getenv("LC_ALL")) == NULL
)
495 if ((ptr
= getenv("LANG")) == NULL
)
501 strlcpy(locale
, ptr
, sizeof(locale
));
505 * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
508 if (!strncmp(locale
, "nb", 2))
511 DEBUG_printf(("4cupsLangGet: new language value is \"%s\"", language
));
514 #endif /* __APPLE__ */
517 * If "language" is NULL at this point, then chances are we are using
518 * a language that is not installed for the base OS.
524 * Switch to the POSIX ("C") locale...
532 * On systems that support the nl_langinfo(CODESET) call, use
533 * this value as the character set...
536 if (!charset
[0] && (csptr
= nl_langinfo(CODESET
)) != NULL
)
539 * Copy all of the letters and numbers in the CODESET string...
542 for (ptr
= charset
; *csptr
; csptr
++)
543 if (_cups_isalnum(*csptr
) && ptr
< (charset
+ sizeof(charset
) - 1))
548 DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via "
549 "nl_langinfo(CODESET)...", charset
));
554 * If we don't have a character set by now, default to UTF-8...
558 strlcpy(charset
, "UTF8", sizeof(charset
));
561 * Parse the language string passed in to a locale string. "C" is the
562 * standard POSIX locale and is copied unchanged. Otherwise the
563 * language string is converted from ll-cc[.charset] (language-country)
564 * to ll_CC[.CHARSET] to match the file naming convention used by all
565 * POSIX-compliant operating systems. Invalid language names are mapped
566 * to the POSIX locale.
571 if (language
== NULL
|| !language
[0] ||
572 !strcmp(language
, "POSIX"))
573 strlcpy(langname
, "C", sizeof(langname
));
577 * Copy the parts of the locale string over safely...
580 for (ptr
= langname
; *language
; language
++)
581 if (*language
== '_' || *language
== '-' || *language
== '.')
583 else if (ptr
< (langname
+ sizeof(langname
) - 1))
584 *ptr
++ = (char)tolower(*language
& 255);
588 if (*language
== '_' || *language
== '-')
591 * Copy the country code...
594 for (language
++, ptr
= country
; *language
; language
++)
595 if (*language
== '.')
597 else if (ptr
< (country
+ sizeof(country
) - 1))
598 *ptr
++ = (char)toupper(*language
& 255);
603 if (*language
== '.' && !charset
[0])
606 * Copy the encoding...
609 for (language
++, ptr
= charset
; *language
; language
++)
610 if (_cups_isalnum(*language
) && ptr
< (charset
+ sizeof(charset
) - 1))
611 *ptr
++ = (char)toupper(*language
& 255);
617 * Force a POSIX locale for an invalid language name...
620 if (strlen(langname
) != 2)
622 strlcpy(langname
, "C", sizeof(langname
));
628 DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"",
629 langname
, country
, charset
));
632 * Figure out the desired encoding...
635 encoding
= CUPS_AUTO_ENCODING
;
640 i
< (int)(sizeof(locale_encodings
) / sizeof(locale_encodings
[0]));
642 if (!_cups_strcasecmp(charset
, locale_encodings
[i
]))
644 encoding
= (cups_encoding_t
)i
;
648 if (encoding
== CUPS_AUTO_ENCODING
)
651 * Map alternate names for various character sets...
654 if (!_cups_strcasecmp(charset
, "iso-2022-jp") ||
655 !_cups_strcasecmp(charset
, "sjis"))
656 encoding
= CUPS_WINDOWS_932
;
657 else if (!_cups_strcasecmp(charset
, "iso-2022-cn"))
658 encoding
= CUPS_WINDOWS_936
;
659 else if (!_cups_strcasecmp(charset
, "iso-2022-kr"))
660 encoding
= CUPS_WINDOWS_949
;
661 else if (!_cups_strcasecmp(charset
, "big5"))
662 encoding
= CUPS_WINDOWS_950
;
666 DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding
,
667 encoding
== CUPS_AUTO_ENCODING
? "auto" :
668 lang_encodings
[encoding
]));
671 * See if we already have this language/country loaded...
675 snprintf(real
, sizeof(real
), "%s_%s", langname
, country
);
677 strlcpy(real
, langname
, sizeof(real
));
679 _cupsMutexLock(&lang_mutex
);
681 if ((lang
= cups_cache_lookup(real
, encoding
)) != NULL
)
683 _cupsMutexUnlock(&lang_mutex
);
685 DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real
));
691 * See if there is a free language available; if so, use that
695 for (lang
= lang_cache
; lang
!= NULL
; lang
= lang
->next
)
702 * Allocate memory for the language and add it to the cache.
705 if ((lang
= calloc(sizeof(cups_lang_t
), 1)) == NULL
)
707 _cupsMutexUnlock(&lang_mutex
);
712 lang
->next
= lang_cache
;
718 * Free all old strings as needed...
721 _cupsMessageFree(lang
->strings
);
722 lang
->strings
= NULL
;
726 * Then assign the language and encoding fields...
730 strlcpy(lang
->language
, real
, sizeof(lang
->language
));
732 if (encoding
!= CUPS_AUTO_ENCODING
)
733 lang
->encoding
= encoding
;
735 lang
->encoding
= CUPS_UTF8
;
741 _cupsMutexUnlock(&lang_mutex
);
748 * '_cupsLangString()' - Get a message string.
750 * The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to
751 * convert the string to the language encoding.
754 const char * /* O - Localized message */
755 _cupsLangString(cups_lang_t
*lang
, /* I - Language */
756 const char *message
) /* I - Message */
758 const char *s
; /* Localized message */
761 * Range check input...
764 if (!lang
|| !message
|| !*message
)
767 _cupsMutexLock(&lang_mutex
);
770 * Load the message catalog if needed...
774 cups_message_load(lang
);
776 s
= _cupsMessageLookup(lang
->strings
, message
);
778 _cupsMutexUnlock(&lang_mutex
);
785 * '_cupsMessageFree()' - Free a messages array.
789 _cupsMessageFree(cups_array_t
*a
) /* I - Message array */
791 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
793 * Release the cups.strings dictionary as needed...
796 if (cupsArrayUserData(a
))
797 CFRelease((CFDictionaryRef
)cupsArrayUserData(a
));
798 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
809 * '_cupsMessageLoad()' - Load a .po file into a messages array.
812 cups_array_t
* /* O - New message array */
813 _cupsMessageLoad(const char *filename
, /* I - Message catalog to load */
814 int unquote
) /* I - Unescape \foo in strings? */
816 cups_file_t
*fp
; /* Message file */
817 cups_array_t
*a
; /* Message array */
818 _cups_message_t
*m
; /* Current message */
819 char s
[4096], /* String buffer */
820 *ptr
, /* Pointer into buffer */
821 *temp
; /* New string */
822 size_t length
, /* Length of combined strings */
823 ptrlen
; /* Length of string */
826 DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename
));
829 * Create an array to hold the messages...
832 if ((a
= _cupsMessageNew(NULL
)) == NULL
)
834 DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
839 * Open the message catalog file...
842 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
844 DEBUG_printf(("5_cupsMessageLoad: Unable to open file: %s",
850 * Read messages from the catalog file until EOF...
852 * The format is the GNU gettext .po format, which is fairly simple:
855 * msgstr "localized text"
857 * The ID and localized text can span multiple lines using the form:
862 * "localized text spanning "
868 while (cupsFileGets(fp
, s
, sizeof(s
)) != NULL
)
871 * Skip blank and comment lines...
874 if (s
[0] == '#' || !s
[0])
878 * Strip the trailing quote...
881 if ((ptr
= strrchr(s
, '\"')) == NULL
)
887 * Find start of value...
890 if ((ptr
= strchr(s
, '\"')) == NULL
)
896 * Unquote the text...
900 cups_unquote(ptr
, ptr
);
903 * Create or add to a message...
906 if (!strncmp(s
, "msgid", 5))
909 * Add previous message as needed...
914 if (m
->str
&& m
->str
[0])
921 * Translation is empty, don't add it... (STR #4033)
932 * Create a new message with the given msgid string...
935 if ((m
= (_cups_message_t
*)calloc(1, sizeof(_cups_message_t
))) == NULL
)
941 if ((m
->id
= strdup(ptr
)) == NULL
)
948 else if (s
[0] == '\"' && m
)
951 * Append to current string...
954 length
= strlen(m
->str
? m
->str
: m
->id
);
955 ptrlen
= strlen(ptr
);
957 if ((temp
= realloc(m
->str
? m
->str
: m
->id
, length
+ ptrlen
+ 1)) == NULL
)
971 * Copy the new portion to the end of the msgstr string - safe
972 * to use memcpy because the buffer is allocated to the correct
978 memcpy(m
->str
+ length
, ptr
, ptrlen
+ 1);
983 * Copy the new portion to the end of the msgid string - safe
984 * to use memcpy because the buffer is allocated to the correct
990 memcpy(m
->id
+ length
, ptr
, ptrlen
+ 1);
993 else if (!strncmp(s
, "msgstr", 6) && m
)
999 if ((m
->str
= strdup(ptr
)) == NULL
)
1011 * Add the last message string to the array as needed...
1016 if (m
->str
&& m
->str
[0])
1023 * Translation is empty, don't add it... (STR #4033)
1034 * Close the message catalog file and return the new array...
1039 DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
1040 cupsArrayCount(a
)));
1047 * '_cupsMessageLookup()' - Lookup a message string.
1050 const char * /* O - Localized message */
1051 _cupsMessageLookup(cups_array_t
*a
, /* I - Message array */
1052 const char *m
) /* I - Message */
1054 _cups_message_t key
, /* Search key */
1055 *match
; /* Matching message */
1059 * Lookup the message string; if it doesn't exist in the catalog,
1060 * then return the message that was passed to us...
1064 match
= (_cups_message_t
*)cupsArrayFind(a
, &key
);
1066 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1067 if (!match
&& cupsArrayUserData(a
))
1070 * Try looking the string up in the cups.strings dictionary...
1073 CFDictionaryRef dict
; /* cups.strings dictionary */
1074 CFStringRef cfm
, /* Message as a CF string */
1075 cfstr
; /* Localized text as a CF string */
1077 dict
= (CFDictionaryRef
)cupsArrayUserData(a
);
1078 cfm
= CFStringCreateWithCString(kCFAllocatorDefault
, m
,
1079 kCFStringEncodingUTF8
);
1080 match
= calloc(1, sizeof(_cups_message_t
));
1081 match
->id
= strdup(m
);
1082 cfstr
= cfm
? CFDictionaryGetValue(dict
, cfm
) : NULL
;
1086 char buffer
[1024]; /* Message buffer */
1088 CFStringGetCString(cfstr
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
);
1089 match
->str
= strdup(buffer
);
1091 DEBUG_printf(("1_cupsMessageLookup: Found \"%s\" as \"%s\"...",
1096 match
->str
= strdup(m
);
1098 DEBUG_printf(("1_cupsMessageLookup: Did not find \"%s\"...", m
));
1101 cupsArrayAdd(a
, match
);
1106 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
1108 if (match
&& match
->str
)
1109 return (match
->str
);
1116 * '_cupsMessageNew()' - Make a new message catalog array.
1119 cups_array_t
* /* O - Array */
1120 _cupsMessageNew(void *context
) /* I - User data */
1122 return (cupsArrayNew3((cups_array_func_t
)cups_message_compare
, context
,
1123 (cups_ahash_func_t
)NULL
, 0,
1124 (cups_acopy_func_t
)NULL
,
1125 (cups_afree_func_t
)cups_message_free
));
1131 * 'appleLangDefault()' - Get the default locale string.
1134 static const char * /* O - Locale string */
1135 appleLangDefault(void)
1137 int i
; /* Looping var */
1138 CFBundleRef bundle
; /* Main bundle (if any) */
1139 CFArrayRef bundleList
; /* List of localizations in bundle */
1140 CFPropertyListRef localizationList
= NULL
;
1141 /* List of localization data */
1142 CFStringRef languageName
; /* Current name */
1143 CFStringRef localeName
; /* Canonical from of name */
1144 char *lang
; /* LANG environment variable */
1145 _cups_globals_t
*cg
= _cupsGlobals();
1146 /* Pointer to library globals */
1149 DEBUG_puts("2appleLangDefault()");
1152 * Only do the lookup and translation the first time.
1155 if (!cg
->language
[0])
1157 if (getenv("SOFTWARE") != NULL
&& (lang
= getenv("LANG")) != NULL
)
1159 DEBUG_printf(("3appleLangDefault: Using LANG=%s", lang
));
1160 strlcpy(cg
->language
, lang
, sizeof(cg
->language
));
1161 return (cg
->language
);
1163 else if ((bundle
= CFBundleGetMainBundle()) != NULL
&&
1164 (bundleList
= CFBundleCopyBundleLocalizations(bundle
)) != NULL
)
1166 CFURLRef resources
= CFBundleCopyResourcesDirectoryURL(bundle
);
1168 DEBUG_puts("3appleLangDefault: Getting localizationList from bundle.");
1172 CFStringRef cfpath
= CFURLCopyPath(resources
);
1178 * See if we have an Info.plist file in the bundle...
1181 CFStringGetCString(cfpath
, path
, sizeof(path
), kCFStringEncodingUTF8
);
1182 DEBUG_printf(("3appleLangDefault: Got a resource URL (\"%s\")", path
));
1183 strlcat(path
, "Contents/Info.plist", sizeof(path
));
1185 if (!access(path
, R_OK
))
1186 localizationList
= CFBundleCopyPreferredLocalizationsFromArray(bundleList
);
1188 DEBUG_puts("3appleLangDefault: No Info.plist, ignoring resource URL...");
1193 CFRelease(resources
);
1196 DEBUG_puts("3appleLangDefault: No resource URL.");
1198 CFRelease(bundleList
);
1201 if (!localizationList
)
1203 DEBUG_puts("3appleLangDefault: Getting localizationList from preferences.");
1206 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1207 kCFPreferencesCurrentApplication
);
1210 if (localizationList
)
1213 if (CFGetTypeID(localizationList
) == CFArrayGetTypeID())
1214 DEBUG_printf(("3appleLangDefault: Got localizationList, %d entries.",
1215 (int)CFArrayGetCount(localizationList
)));
1217 DEBUG_puts("3appleLangDefault: Got localizationList but not an array.");
1220 if (CFGetTypeID(localizationList
) == CFArrayGetTypeID() &&
1221 CFArrayGetCount(localizationList
) > 0)
1223 languageName
= CFArrayGetValueAtIndex(localizationList
, 0);
1226 CFGetTypeID(languageName
) == CFStringGetTypeID())
1228 localeName
= CFLocaleCreateCanonicalLocaleIdentifierFromString(
1229 kCFAllocatorDefault
, languageName
);
1233 CFStringGetCString(localeName
, cg
->language
, sizeof(cg
->language
),
1234 kCFStringEncodingASCII
);
1235 CFRelease(localeName
);
1237 DEBUG_printf(("3appleLangDefault: cg->language=\"%s\"",
1241 * Map new language identifiers to locales...
1245 i
< (int)(sizeof(apple_language_locale
) /
1246 sizeof(apple_language_locale
[0]));
1249 if (!strcmp(cg
->language
, apple_language_locale
[i
].language
))
1251 DEBUG_printf(("3appleLangDefault: mapping \"%s\" to \"%s\"...",
1252 cg
->language
, apple_language_locale
[i
].locale
));
1253 strlcpy(cg
->language
, apple_language_locale
[i
].locale
,
1254 sizeof(cg
->language
));
1260 * Convert language subtag into region subtag...
1263 if (cg
->language
[2] == '-')
1264 cg
->language
[2] = '_';
1266 if (!strchr(cg
->language
, '.'))
1267 strlcat(cg
->language
, ".UTF-8", sizeof(cg
->language
));
1270 DEBUG_puts("3appleLangDefault: Unable to get localeName.");
1274 CFRelease(localizationList
);
1278 * If we didn't find the language, default to en_US...
1281 if (!cg
->language
[0])
1283 DEBUG_puts("3appleLangDefault: Defaulting to en_US.");
1284 strlcpy(cg
->language
, "en_US.UTF-8", sizeof(cg
->language
));
1288 DEBUG_printf(("3appleLangDefault: Using previous locale \"%s\".", cg
->language
));
1291 * Return the cached locale...
1294 return (cg
->language
);
1298 # ifdef CUPS_BUNDLEDIR
1300 * 'appleMessageLoad()' - Load a message catalog from a localizable bundle.
1303 static cups_array_t
* /* O - Message catalog */
1304 appleMessageLoad(const char *locale
) /* I - Locale ID */
1306 char filename
[1024], /* Path to cups.strings file */
1307 applelang
[256], /* Apple language ID */
1308 baselang
[3]; /* Base language */
1309 CFURLRef url
; /* URL to cups.strings file */
1310 CFReadStreamRef stream
= NULL
; /* File stream */
1311 CFPropertyListRef plist
= NULL
; /* Localization file */
1313 CFErrorRef error
= NULL
; /* Error when opening file */
1317 DEBUG_printf(("appleMessageLoad(locale=\"%s\")", locale
));
1320 * Load the cups.strings file...
1323 snprintf(filename
, sizeof(filename
),
1324 CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings",
1325 _cupsAppleLanguage(locale
, applelang
, sizeof(applelang
)));
1327 if (access(filename
, 0))
1330 * <rdar://problem/22086642>
1332 * Try with original locale string...
1335 snprintf(filename
, sizeof(filename
), CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings", locale
);
1338 if (access(filename
, 0))
1341 * <rdar://problem/25292403>
1343 * Try with just the language code...
1346 strlcpy(baselang
, locale
, sizeof(baselang
));
1347 snprintf(filename
, sizeof(filename
), CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings", baselang
);
1350 DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename
));
1352 if (access(filename
, 0))
1355 * Try alternate lproj directory names...
1358 if (!strncmp(locale
, "en", 2))
1360 else if (!strncmp(locale
, "nb", 2))
1362 else if (!strncmp(locale
, "nl", 2))
1364 else if (!strncmp(locale
, "fr", 2))
1366 else if (!strncmp(locale
, "de", 2))
1368 else if (!strncmp(locale
, "it", 2))
1370 else if (!strncmp(locale
, "ja", 2))
1371 locale
= "Japanese";
1372 else if (!strncmp(locale
, "es", 2))
1374 else if (!strcmp(locale
, "zh_HK"))
1377 * <rdar://problem/22130168>
1379 * Try zh_TW first, then zh... Sigh...
1382 if (!access(CUPS_BUNDLEDIR
"/Resources/zh_TW.lproj/cups.strings", 0))
1387 else if (strstr(locale
, "_") != NULL
|| strstr(locale
, "-") != NULL
)
1390 * Drop country code, just try language...
1393 strlcpy(baselang
, locale
, sizeof(baselang
));
1397 snprintf(filename
, sizeof(filename
),
1398 CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings", locale
);
1399 DEBUG_printf(("1appleMessageLoad: alternate filename=\"%s\"", filename
));
1402 url
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
,
1404 (CFIndex
)strlen(filename
), false);
1407 stream
= CFReadStreamCreateWithFile(kCFAllocatorDefault
, url
);
1411 * Read the property list containing the localization data.
1413 * NOTE: This code currently generates a clang "potential leak"
1414 * warning, but the object is released in _cupsMessageFree().
1417 CFReadStreamOpen(stream
);
1420 plist
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, stream
, 0,
1421 kCFPropertyListImmutable
, NULL
,
1425 CFStringRef msg
= CFErrorCopyDescription(error
);
1428 CFStringGetCString(msg
, filename
, sizeof(filename
),
1429 kCFStringEncodingUTF8
);
1430 DEBUG_printf(("1appleMessageLoad: %s", filename
));
1437 plist
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, stream
, 0,
1438 kCFPropertyListImmutable
, NULL
,
1442 if (plist
&& CFGetTypeID(plist
) != CFDictionaryGetTypeID())
1454 DEBUG_printf(("1appleMessageLoad: url=%p, stream=%p, plist=%p", url
, stream
,
1458 * Create and return an empty array to act as a cache for messages, passing the
1459 * plist as the user data.
1462 return (_cupsMessageNew((void *)plist
));
1464 # endif /* CUPS_BUNDLEDIR */
1465 #endif /* __APPLE__ */
1469 * 'cups_cache_lookup()' - Lookup a language in the cache...
1472 static cups_lang_t
* /* O - Language data or NULL */
1474 const char *name
, /* I - Name of locale */
1475 cups_encoding_t encoding
) /* I - Encoding of locale */
1477 cups_lang_t
*lang
; /* Current language */
1480 DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name
,
1481 encoding
, encoding
== CUPS_AUTO_ENCODING
? "auto" :
1482 lang_encodings
[encoding
]));
1485 * Loop through the cache and return a match if found...
1488 for (lang
= lang_cache
; lang
!= NULL
; lang
= lang
->next
)
1490 DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
1491 "encoding=%d(%s)", (void *)lang
, lang
->language
, lang
->encoding
,
1492 lang_encodings
[lang
->encoding
]));
1494 if (!strcmp(lang
->language
, name
) &&
1495 (encoding
== CUPS_AUTO_ENCODING
|| encoding
== lang
->encoding
))
1499 DEBUG_puts("8cups_cache_lookup: returning match!");
1505 DEBUG_puts("8cups_cache_lookup: returning NULL!");
1512 * 'cups_message_compare()' - Compare two messages.
1515 static int /* O - Result of comparison */
1516 cups_message_compare(
1517 _cups_message_t
*m1
, /* I - First message */
1518 _cups_message_t
*m2
) /* I - Second message */
1520 return (strcmp(m1
->id
, m2
->id
));
1525 * 'cups_message_free()' - Free a message.
1529 cups_message_free(_cups_message_t
*m
) /* I - Message */
1542 * 'cups_message_load()' - Load the message catalog for a language.
1546 cups_message_load(cups_lang_t
*lang
) /* I - Language */
1548 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1549 lang
->strings
= appleMessageLoad(lang
->language
);
1552 char filename
[1024]; /* Filename for language locale file */
1553 _cups_globals_t
*cg
= _cupsGlobals();
1554 /* Pointer to library globals */
1557 snprintf(filename
, sizeof(filename
), "%s/%s/cups_%s.po", cg
->localedir
,
1558 lang
->language
, lang
->language
);
1560 if (strchr(lang
->language
, '_') && access(filename
, 0))
1563 * Country localization not available, look for generic localization...
1566 snprintf(filename
, sizeof(filename
), "%s/%.2s/cups_%.2s.po", cg
->localedir
,
1567 lang
->language
, lang
->language
);
1569 if (access(filename
, 0))
1572 * No generic localization, so use POSIX...
1575 DEBUG_printf(("4cups_message_load: access(\"%s\", 0): %s", filename
,
1578 snprintf(filename
, sizeof(filename
), "%s/C/cups_C.po", cg
->localedir
);
1583 * Read the strings from the file...
1586 lang
->strings
= _cupsMessageLoad(filename
, 1);
1587 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
1592 * 'cups_unquote()' - Unquote characters in strings...
1596 cups_unquote(char *d
, /* O - Unquoted string */
1597 const char *s
) /* I - Original string */
1610 *d
= *d
* 8 + *s
- '0';