2 * I18N/language support for CUPS.
4 * Copyright 2007-2017 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 { /* Language to locale ID LUT */
125 { "zh-Hans", "zh_CN" },
126 { "zh_HANS", "zh_CN" },
127 { "zh-Hant", "zh_TW" },
128 { "zh_HANT", "zh_TW" },
129 { "zh-Hant_CN", "zh_TW" }
131 #endif /* __APPLE__ */
140 static const char *appleLangDefault(void);
141 # ifdef CUPS_BUNDLEDIR
142 # ifndef CF_RETURNS_RETAINED
143 # if __has_feature(attribute_cf_returns_retained)
144 # define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
146 # define CF_RETURNS_RETAINED
147 # endif /* __has_feature(attribute_cf_returns_retained) */
148 # endif /* !CF_RETURNED_RETAINED */
149 static cups_array_t
*appleMessageLoad(const char *locale
)
151 # endif /* CUPS_BUNDLEDIR */
152 #endif /* __APPLE__ */
153 static cups_lang_t
*cups_cache_lookup(const char *name
,
154 cups_encoding_t encoding
);
155 static int cups_message_compare(_cups_message_t
*m1
,
156 _cups_message_t
*m2
);
157 static void cups_message_free(_cups_message_t
*m
);
158 static void cups_message_load(cups_lang_t
*lang
);
159 static void cups_unquote(char *d
, const char *s
);
164 * '_cupsAppleLanguage()' - Get the Apple language identifier associated with a
168 const char * /* O - Language ID */
169 _cupsAppleLanguage(const char *locale
, /* I - Locale ID */
170 char *language
,/* I - Language ID buffer */
171 size_t langsize
) /* I - Size of language ID buffer */
173 int i
; /* Looping var */
174 CFStringRef localeid
, /* CF locale identifier */
175 langid
; /* CF language identifier */
179 * Copy the locale name and convert, as needed, to the Apple-specific
180 * locale identifier...
183 switch (strlen(locale
))
190 strlcpy(language
, "en", langsize
);
194 strlcpy(language
, locale
, langsize
);
198 strlcpy(language
, locale
, langsize
);
200 if (language
[2] == '-')
203 * Convert ll-cc to ll_CC...
207 language
[3] = (char)toupper(language
[3] & 255);
208 language
[4] = (char)toupper(language
[4] & 255);
214 i
< (int)(sizeof(apple_language_locale
) /
215 sizeof(apple_language_locale
[0]));
217 if (!strcmp(locale
, apple_language_locale
[i
].locale
))
219 strlcpy(language
, apple_language_locale
[i
].language
, sizeof(language
));
224 * Attempt to map the locale ID to a language ID...
227 if ((localeid
= CFStringCreateWithCString(kCFAllocatorDefault
, language
,
228 kCFStringEncodingASCII
)) != NULL
)
230 if ((langid
= CFLocaleCreateCanonicalLanguageIdentifierFromString(
231 kCFAllocatorDefault
, localeid
)) != NULL
)
233 CFStringGetCString(langid
, language
, (CFIndex
)langsize
, kCFStringEncodingASCII
);
241 * Return what we got...
249 * '_cupsAppleLocale()' - Get the locale associated with an Apple language ID.
252 const char * /* O - Locale */
253 _cupsAppleLocale(CFStringRef languageName
, /* I - Apple language ID */
254 char *locale
, /* I - Buffer for locale */
255 size_t localesize
) /* I - Size of buffer */
257 int i
; /* Looping var */
258 CFStringRef localeName
; /* Locale as a CF string */
260 char temp
[1024]; /* Temporary string */
263 if (!CFStringGetCString(languageName
, temp
, (CFIndex
)sizeof(temp
), kCFStringEncodingASCII
))
266 DEBUG_printf(("_cupsAppleLocale(languageName=%p(%s), locale=%p, localsize=%d)", languageName
, temp
, locale
, (int)localesize
));
269 localeName
= CFLocaleCreateCanonicalLocaleIdentifierFromString(kCFAllocatorDefault
, languageName
);
274 * Copy the locale name and tweak as needed...
277 if (!CFStringGetCString(localeName
, locale
, (CFIndex
)localesize
, kCFStringEncodingASCII
))
280 DEBUG_printf(("_cupsAppleLocale: locale=\"%s\"", locale
));
282 CFRelease(localeName
);
285 * Map new language identifiers to locales...
289 i
< (int)(sizeof(apple_language_locale
) /
290 sizeof(apple_language_locale
[0]));
293 size_t len
= strlen(apple_language_locale
[i
].language
);
295 if (!strcmp(locale
, apple_language_locale
[i
].language
) ||
296 (!strncmp(locale
, apple_language_locale
[i
].language
, len
) && (locale
[len
] == '_' || locale
[len
] == '-')))
298 DEBUG_printf(("_cupsAppleLocale: Updating locale to \"%s\".", apple_language_locale
[i
].locale
));
299 strlcpy(locale
, apple_language_locale
[i
].locale
, localesize
);
307 * Just try the Apple language name...
310 if (!CFStringGetCString(languageName
, locale
, (CFIndex
)localesize
, kCFStringEncodingASCII
))
316 DEBUG_puts("_cupsAppleLocale: Returning NULL.");
321 * Convert language subtag into region subtag...
324 if (locale
[2] == '-')
326 else if (locale
[3] == '-')
329 if (!strchr(locale
, '.'))
330 strlcat(locale
, ".UTF-8", localesize
);
332 DEBUG_printf(("_cupsAppleLocale: Returning \"%s\".", locale
));
336 #endif /* __APPLE__ */
340 * '_cupsEncodingName()' - Return the character encoding name string
341 * for the given encoding enumeration.
344 const char * /* O - Character encoding */
346 cups_encoding_t encoding
) /* I - Encoding value */
348 if (encoding
< CUPS_US_ASCII
||
349 encoding
>= (cups_encoding_t
)(sizeof(lang_encodings
) / sizeof(lang_encodings
[0])))
351 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = out of range (\"%s\")",
352 encoding
, lang_encodings
[0]));
353 return (lang_encodings
[0]);
357 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = \"%s\"",
358 encoding
, lang_encodings
[encoding
]));
359 return (lang_encodings
[encoding
]);
365 * 'cupsLangDefault()' - Return the default language.
368 cups_lang_t
* /* O - Language data */
369 cupsLangDefault(void)
371 return (cupsLangGet(NULL
));
376 * 'cupsLangEncoding()' - Return the character encoding (us-ascii, etc.)
377 * for the given language.
380 const char * /* O - Character encoding */
381 cupsLangEncoding(cups_lang_t
*lang
) /* I - Language data */
384 return ((char*)lang_encodings
[0]);
386 return ((char*)lang_encodings
[lang
->encoding
]);
391 * 'cupsLangFlush()' - Flush all language data out of the cache.
397 cups_lang_t
*lang
, /* Current language */
398 *next
; /* Next language */
402 * Free all languages in the cache...
405 _cupsMutexLock(&lang_mutex
);
407 for (lang
= lang_cache
; lang
!= NULL
; lang
= next
)
410 * Free all messages...
413 _cupsMessageFree(lang
->strings
);
416 * Then free the language structure itself...
425 _cupsMutexUnlock(&lang_mutex
);
430 * 'cupsLangFree()' - Free language data.
432 * This does not actually free anything; use @link cupsLangFlush@ for that.
436 cupsLangFree(cups_lang_t
*lang
) /* I - Language to free */
438 _cupsMutexLock(&lang_mutex
);
440 if (lang
!= NULL
&& lang
->used
> 0)
443 _cupsMutexUnlock(&lang_mutex
);
448 * 'cupsLangGet()' - Get a language.
451 cups_lang_t
* /* O - Language data */
452 cupsLangGet(const char *language
) /* I - Language or locale */
454 int i
; /* Looping var */
456 char locale
[255]; /* Copy of locale name */
457 #endif /* !__APPLE__ */
458 char langname
[16], /* Requested language name */
459 country
[16], /* Country code */
460 charset
[16], /* Character set */
461 *csptr
, /* Pointer to CODESET string */
462 *ptr
, /* Pointer into language/charset */
463 real
[48]; /* Real language name */
464 cups_encoding_t encoding
; /* Encoding to use */
465 cups_lang_t
*lang
; /* Current language... */
466 static const char * const locale_encodings
[] =
467 { /* Locale charset names */
468 "ASCII", "ISO88591", "ISO88592", "ISO88593",
469 "ISO88594", "ISO88595", "ISO88596", "ISO88597",
470 "ISO88598", "ISO88599", "ISO885910", "UTF8",
471 "ISO885913", "ISO885914", "ISO885915", "CP874",
472 "CP1250", "CP1251", "CP1252", "CP1253",
473 "CP1254", "CP1255", "CP1256", "CP1257",
474 "CP1258", "KOI8R", "KOI8U", "ISO885911",
475 "ISO885916", "MACROMAN", "", "",
486 "CP932", "CP936", "CP949", "CP950",
487 "CP1361", "", "", "",
504 "EUCCN", "EUCJP", "EUCKR", "EUCTW",
509 DEBUG_printf(("2cupsLangGet(language=\"%s\")", language
));
513 * Set the character set to UTF-8...
516 strlcpy(charset
, "UTF8", sizeof(charset
));
519 * Apple's setlocale doesn't give us the user's localization
520 * preference so we have to look it up this way...
525 if (!getenv("SOFTWARE") || (language
= getenv("LANG")) == NULL
)
526 language
= appleLangDefault();
528 DEBUG_printf(("4cupsLangGet: language=\"%s\"", language
));
533 * Set the charset to "unknown"...
539 * Use setlocale() to determine the currently set locale, and then
540 * fallback to environment variables to avoid setting the locale,
541 * since setlocale() is not thread-safe!
547 * First see if the locale has been set; if it is still "C" or
548 * "POSIX", use the environment to get the default...
552 ptr
= setlocale(LC_MESSAGES
, NULL
);
554 ptr
= setlocale(LC_ALL
, NULL
);
555 # endif /* LC_MESSAGES */
557 DEBUG_printf(("4cupsLangGet: current locale is \"%s\"", ptr
));
559 if (!ptr
|| !strcmp(ptr
, "C") || !strcmp(ptr
, "POSIX"))
562 * Get the character set from the LC_CTYPE locale setting...
565 if ((ptr
= getenv("LC_CTYPE")) == NULL
)
566 if ((ptr
= getenv("LC_ALL")) == NULL
)
567 if ((ptr
= getenv("LANG")) == NULL
)
570 if ((csptr
= strchr(ptr
, '.')) != NULL
)
573 * Extract the character set from the environment...
576 for (ptr
= charset
, csptr
++; *csptr
; csptr
++)
577 if (ptr
< (charset
+ sizeof(charset
) - 1) && _cups_isalnum(*csptr
))
584 * Get the locale for messages from the LC_MESSAGES locale setting...
587 if ((ptr
= getenv("LC_MESSAGES")) == NULL
)
588 if ((ptr
= getenv("LC_ALL")) == NULL
)
589 if ((ptr
= getenv("LANG")) == NULL
)
595 strlcpy(locale
, ptr
, sizeof(locale
));
599 * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
602 if (!strncmp(locale
, "nb", 2))
605 DEBUG_printf(("4cupsLangGet: new language value is \"%s\"", language
));
608 #endif /* __APPLE__ */
611 * If "language" is NULL at this point, then chances are we are using
612 * a language that is not installed for the base OS.
618 * Switch to the POSIX ("C") locale...
626 * On systems that support the nl_langinfo(CODESET) call, use
627 * this value as the character set...
630 if (!charset
[0] && (csptr
= nl_langinfo(CODESET
)) != NULL
)
633 * Copy all of the letters and numbers in the CODESET string...
636 for (ptr
= charset
; *csptr
; csptr
++)
637 if (_cups_isalnum(*csptr
) && ptr
< (charset
+ sizeof(charset
) - 1))
642 DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via "
643 "nl_langinfo(CODESET)...", charset
));
648 * If we don't have a character set by now, default to UTF-8...
652 strlcpy(charset
, "UTF8", sizeof(charset
));
655 * Parse the language string passed in to a locale string. "C" is the
656 * standard POSIX locale and is copied unchanged. Otherwise the
657 * language string is converted from ll-cc[.charset] (language-country)
658 * to ll_CC[.CHARSET] to match the file naming convention used by all
659 * POSIX-compliant operating systems. Invalid language names are mapped
660 * to the POSIX locale.
665 if (language
== NULL
|| !language
[0] ||
666 !strcmp(language
, "POSIX"))
667 strlcpy(langname
, "C", sizeof(langname
));
671 * Copy the parts of the locale string over safely...
674 for (ptr
= langname
; *language
; language
++)
675 if (*language
== '_' || *language
== '-' || *language
== '.')
677 else if (ptr
< (langname
+ sizeof(langname
) - 1))
678 *ptr
++ = (char)tolower(*language
& 255);
682 if (*language
== '_' || *language
== '-')
685 * Copy the country code...
688 for (language
++, ptr
= country
; *language
; language
++)
689 if (*language
== '.')
691 else if (ptr
< (country
+ sizeof(country
) - 1))
692 *ptr
++ = (char)toupper(*language
& 255);
697 if (*language
== '.' && !charset
[0])
700 * Copy the encoding...
703 for (language
++, ptr
= charset
; *language
; language
++)
704 if (_cups_isalnum(*language
) && ptr
< (charset
+ sizeof(charset
) - 1))
705 *ptr
++ = (char)toupper(*language
& 255);
711 * Force a POSIX locale for an invalid language name...
714 if (strlen(langname
) != 2 && strlen(langname
) != 3)
716 strlcpy(langname
, "C", sizeof(langname
));
722 DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"",
723 langname
, country
, charset
));
726 * Figure out the desired encoding...
729 encoding
= CUPS_AUTO_ENCODING
;
734 i
< (int)(sizeof(locale_encodings
) / sizeof(locale_encodings
[0]));
736 if (!_cups_strcasecmp(charset
, locale_encodings
[i
]))
738 encoding
= (cups_encoding_t
)i
;
742 if (encoding
== CUPS_AUTO_ENCODING
)
745 * Map alternate names for various character sets...
748 if (!_cups_strcasecmp(charset
, "iso-2022-jp") ||
749 !_cups_strcasecmp(charset
, "sjis"))
750 encoding
= CUPS_WINDOWS_932
;
751 else if (!_cups_strcasecmp(charset
, "iso-2022-cn"))
752 encoding
= CUPS_WINDOWS_936
;
753 else if (!_cups_strcasecmp(charset
, "iso-2022-kr"))
754 encoding
= CUPS_WINDOWS_949
;
755 else if (!_cups_strcasecmp(charset
, "big5"))
756 encoding
= CUPS_WINDOWS_950
;
760 DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding
,
761 encoding
== CUPS_AUTO_ENCODING
? "auto" :
762 lang_encodings
[encoding
]));
765 * See if we already have this language/country loaded...
769 snprintf(real
, sizeof(real
), "%s_%s", langname
, country
);
771 strlcpy(real
, langname
, sizeof(real
));
773 _cupsMutexLock(&lang_mutex
);
775 if ((lang
= cups_cache_lookup(real
, encoding
)) != NULL
)
777 _cupsMutexUnlock(&lang_mutex
);
779 DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real
));
785 * See if there is a free language available; if so, use that
789 for (lang
= lang_cache
; lang
!= NULL
; lang
= lang
->next
)
796 * Allocate memory for the language and add it to the cache.
799 if ((lang
= calloc(sizeof(cups_lang_t
), 1)) == NULL
)
801 _cupsMutexUnlock(&lang_mutex
);
806 lang
->next
= lang_cache
;
812 * Free all old strings as needed...
815 _cupsMessageFree(lang
->strings
);
816 lang
->strings
= NULL
;
820 * Then assign the language and encoding fields...
824 strlcpy(lang
->language
, real
, sizeof(lang
->language
));
826 if (encoding
!= CUPS_AUTO_ENCODING
)
827 lang
->encoding
= encoding
;
829 lang
->encoding
= CUPS_UTF8
;
835 _cupsMutexUnlock(&lang_mutex
);
842 * '_cupsLangString()' - Get a message string.
844 * The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to
845 * convert the string to the language encoding.
848 const char * /* O - Localized message */
849 _cupsLangString(cups_lang_t
*lang
, /* I - Language */
850 const char *message
) /* I - Message */
852 const char *s
; /* Localized message */
855 * Range check input...
858 if (!lang
|| !message
|| !*message
)
861 _cupsMutexLock(&lang_mutex
);
864 * Load the message catalog if needed...
868 cups_message_load(lang
);
870 s
= _cupsMessageLookup(lang
->strings
, message
);
872 _cupsMutexUnlock(&lang_mutex
);
879 * '_cupsMessageFree()' - Free a messages array.
883 _cupsMessageFree(cups_array_t
*a
) /* I - Message array */
885 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
887 * Release the cups.strings dictionary as needed...
890 if (cupsArrayUserData(a
))
891 CFRelease((CFDictionaryRef
)cupsArrayUserData(a
));
892 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
903 * '_cupsMessageLoad()' - Load a .po file into a messages array.
906 cups_array_t
* /* O - New message array */
907 _cupsMessageLoad(const char *filename
, /* I - Message catalog to load */
908 int unquote
) /* I - Unescape \foo in strings? */
910 cups_file_t
*fp
; /* Message file */
911 cups_array_t
*a
; /* Message array */
912 _cups_message_t
*m
; /* Current message */
913 char s
[4096], /* String buffer */
914 *ptr
, /* Pointer into buffer */
915 *temp
; /* New string */
916 size_t length
, /* Length of combined strings */
917 ptrlen
; /* Length of string */
920 DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename
));
923 * Create an array to hold the messages...
926 if ((a
= _cupsMessageNew(NULL
)) == NULL
)
928 DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
933 * Open the message catalog file...
936 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
938 DEBUG_printf(("5_cupsMessageLoad: Unable to open file: %s",
944 * Read messages from the catalog file until EOF...
946 * The format is the GNU gettext .po format, which is fairly simple:
949 * msgstr "localized text"
951 * The ID and localized text can span multiple lines using the form:
956 * "localized text spanning "
962 while (cupsFileGets(fp
, s
, sizeof(s
)) != NULL
)
965 * Skip blank and comment lines...
968 if (s
[0] == '#' || !s
[0])
972 * Strip the trailing quote...
975 if ((ptr
= strrchr(s
, '\"')) == NULL
)
981 * Find start of value...
984 if ((ptr
= strchr(s
, '\"')) == NULL
)
990 * Unquote the text...
994 cups_unquote(ptr
, ptr
);
997 * Create or add to a message...
1000 if (!strncmp(s
, "msgid", 5))
1003 * Add previous message as needed...
1008 if (m
->str
&& m
->str
[0])
1015 * Translation is empty, don't add it... (STR #4033)
1026 * Create a new message with the given msgid string...
1029 if ((m
= (_cups_message_t
*)calloc(1, sizeof(_cups_message_t
))) == NULL
)
1035 if ((m
->id
= strdup(ptr
)) == NULL
)
1042 else if (s
[0] == '\"' && m
)
1045 * Append to current string...
1048 length
= strlen(m
->str
? m
->str
: m
->id
);
1049 ptrlen
= strlen(ptr
);
1051 if ((temp
= realloc(m
->str
? m
->str
: m
->id
, length
+ ptrlen
+ 1)) == NULL
)
1065 * Copy the new portion to the end of the msgstr string - safe
1066 * to use memcpy because the buffer is allocated to the correct
1072 memcpy(m
->str
+ length
, ptr
, ptrlen
+ 1);
1077 * Copy the new portion to the end of the msgid string - safe
1078 * to use memcpy because the buffer is allocated to the correct
1084 memcpy(m
->id
+ length
, ptr
, ptrlen
+ 1);
1087 else if (!strncmp(s
, "msgstr", 6) && m
)
1093 if ((m
->str
= strdup(ptr
)) == NULL
)
1105 * Add the last message string to the array as needed...
1110 if (m
->str
&& m
->str
[0])
1117 * Translation is empty, don't add it... (STR #4033)
1128 * Close the message catalog file and return the new array...
1133 DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
1134 cupsArrayCount(a
)));
1141 * '_cupsMessageLookup()' - Lookup a message string.
1144 const char * /* O - Localized message */
1145 _cupsMessageLookup(cups_array_t
*a
, /* I - Message array */
1146 const char *m
) /* I - Message */
1148 _cups_message_t key
, /* Search key */
1149 *match
; /* Matching message */
1153 * Lookup the message string; if it doesn't exist in the catalog,
1154 * then return the message that was passed to us...
1158 match
= (_cups_message_t
*)cupsArrayFind(a
, &key
);
1160 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1161 if (!match
&& cupsArrayUserData(a
))
1164 * Try looking the string up in the cups.strings dictionary...
1167 CFDictionaryRef dict
; /* cups.strings dictionary */
1168 CFStringRef cfm
, /* Message as a CF string */
1169 cfstr
; /* Localized text as a CF string */
1171 dict
= (CFDictionaryRef
)cupsArrayUserData(a
);
1172 cfm
= CFStringCreateWithCString(kCFAllocatorDefault
, m
,
1173 kCFStringEncodingUTF8
);
1174 match
= calloc(1, sizeof(_cups_message_t
));
1175 match
->id
= strdup(m
);
1176 cfstr
= cfm
? CFDictionaryGetValue(dict
, cfm
) : NULL
;
1180 char buffer
[1024]; /* Message buffer */
1182 CFStringGetCString(cfstr
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
);
1183 match
->str
= strdup(buffer
);
1185 DEBUG_printf(("1_cupsMessageLookup: Found \"%s\" as \"%s\"...",
1190 match
->str
= strdup(m
);
1192 DEBUG_printf(("1_cupsMessageLookup: Did not find \"%s\"...", m
));
1195 cupsArrayAdd(a
, match
);
1200 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
1202 if (match
&& match
->str
)
1203 return (match
->str
);
1210 * '_cupsMessageNew()' - Make a new message catalog array.
1213 cups_array_t
* /* O - Array */
1214 _cupsMessageNew(void *context
) /* I - User data */
1216 return (cupsArrayNew3((cups_array_func_t
)cups_message_compare
, context
,
1217 (cups_ahash_func_t
)NULL
, 0,
1218 (cups_acopy_func_t
)NULL
,
1219 (cups_afree_func_t
)cups_message_free
));
1225 * 'appleLangDefault()' - Get the default locale string.
1228 static const char * /* O - Locale string */
1229 appleLangDefault(void)
1231 CFBundleRef bundle
; /* Main bundle (if any) */
1232 CFArrayRef bundleList
; /* List of localizations in bundle */
1233 CFPropertyListRef localizationList
= NULL
;
1234 /* List of localization data */
1235 CFStringRef languageName
; /* Current name */
1236 char *lang
; /* LANG environment variable */
1237 _cups_globals_t
*cg
= _cupsGlobals();
1238 /* Pointer to library globals */
1241 DEBUG_puts("2appleLangDefault()");
1244 * Only do the lookup and translation the first time.
1247 if (!cg
->language
[0])
1249 if (getenv("SOFTWARE") != NULL
&& (lang
= getenv("LANG")) != NULL
)
1251 DEBUG_printf(("3appleLangDefault: Using LANG=%s", lang
));
1252 strlcpy(cg
->language
, lang
, sizeof(cg
->language
));
1253 return (cg
->language
);
1255 else if ((bundle
= CFBundleGetMainBundle()) != NULL
&&
1256 (bundleList
= CFBundleCopyBundleLocalizations(bundle
)) != NULL
)
1258 CFURLRef resources
= CFBundleCopyResourcesDirectoryURL(bundle
);
1260 DEBUG_puts("3appleLangDefault: Getting localizationList from bundle.");
1264 CFStringRef cfpath
= CFURLCopyPath(resources
);
1270 * See if we have an Info.plist file in the bundle...
1273 CFStringGetCString(cfpath
, path
, sizeof(path
), kCFStringEncodingUTF8
);
1274 DEBUG_printf(("3appleLangDefault: Got a resource URL (\"%s\")", path
));
1275 strlcat(path
, "Contents/Info.plist", sizeof(path
));
1277 if (!access(path
, R_OK
))
1278 localizationList
= CFBundleCopyPreferredLocalizationsFromArray(bundleList
);
1280 DEBUG_puts("3appleLangDefault: No Info.plist, ignoring resource URL...");
1285 CFRelease(resources
);
1288 DEBUG_puts("3appleLangDefault: No resource URL.");
1290 CFRelease(bundleList
);
1293 if (!localizationList
)
1295 DEBUG_puts("3appleLangDefault: Getting localizationList from preferences.");
1298 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1299 kCFPreferencesCurrentApplication
);
1302 if (localizationList
)
1305 if (CFGetTypeID(localizationList
) == CFArrayGetTypeID())
1306 DEBUG_printf(("3appleLangDefault: Got localizationList, %d entries.",
1307 (int)CFArrayGetCount(localizationList
)));
1309 DEBUG_puts("3appleLangDefault: Got localizationList but not an array.");
1312 if (CFGetTypeID(localizationList
) == CFArrayGetTypeID() &&
1313 CFArrayGetCount(localizationList
) > 0)
1315 languageName
= CFArrayGetValueAtIndex(localizationList
, 0);
1318 CFGetTypeID(languageName
) == CFStringGetTypeID())
1320 if (_cupsAppleLocale(languageName
, cg
->language
, sizeof(cg
->language
)))
1321 DEBUG_printf(("3appleLangDefault: cg->language=\"%s\"",
1324 DEBUG_puts("3appleLangDefault: Unable to get locale.");
1328 CFRelease(localizationList
);
1332 * If we didn't find the language, default to en_US...
1335 if (!cg
->language
[0])
1337 DEBUG_puts("3appleLangDefault: Defaulting to en_US.");
1338 strlcpy(cg
->language
, "en_US.UTF-8", sizeof(cg
->language
));
1342 DEBUG_printf(("3appleLangDefault: Using previous locale \"%s\".", cg
->language
));
1345 * Return the cached locale...
1348 return (cg
->language
);
1352 # ifdef CUPS_BUNDLEDIR
1354 * 'appleMessageLoad()' - Load a message catalog from a localizable bundle.
1357 static cups_array_t
* /* O - Message catalog */
1358 appleMessageLoad(const char *locale
) /* I - Locale ID */
1360 char filename
[1024], /* Path to cups.strings file */
1361 applelang
[256], /* Apple language ID */
1362 baselang
[4]; /* Base language */
1363 CFURLRef url
; /* URL to cups.strings file */
1364 CFReadStreamRef stream
= NULL
; /* File stream */
1365 CFPropertyListRef plist
= NULL
; /* Localization file */
1367 CFErrorRef error
= NULL
; /* Error when opening file */
1371 DEBUG_printf(("appleMessageLoad(locale=\"%s\")", locale
));
1374 * Load the cups.strings file...
1377 snprintf(filename
, sizeof(filename
),
1378 CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings",
1379 _cupsAppleLanguage(locale
, applelang
, sizeof(applelang
)));
1381 if (access(filename
, 0))
1384 * <rdar://problem/22086642>
1386 * Try with original locale string...
1389 snprintf(filename
, sizeof(filename
), CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings", locale
);
1392 if (access(filename
, 0))
1395 * <rdar://problem/25292403>
1397 * Try with just the language code...
1400 strlcpy(baselang
, locale
, sizeof(baselang
));
1401 if (baselang
[3] == '-' || baselang
[3] == '_')
1404 snprintf(filename
, sizeof(filename
), CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings", baselang
);
1407 DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename
));
1409 if (access(filename
, 0))
1412 * Try alternate lproj directory names...
1415 if (!strncmp(locale
, "en", 2))
1417 else if (!strncmp(locale
, "nb", 2))
1419 else if (!strncmp(locale
, "nl", 2))
1421 else if (!strncmp(locale
, "fr", 2))
1423 else if (!strncmp(locale
, "de", 2))
1425 else if (!strncmp(locale
, "it", 2))
1427 else if (!strncmp(locale
, "ja", 2))
1428 locale
= "Japanese";
1429 else if (!strncmp(locale
, "es", 2))
1431 else if (!strcmp(locale
, "zh_HK") || !strncasecmp(locale
, "zh-Hant", 7) || !strncasecmp(locale
, "zh_Hant", 7))
1434 * <rdar://problem/22130168>
1435 * <rdar://problem/27245567>
1437 * Try zh_TW first, then zh... Sigh...
1440 if (!access(CUPS_BUNDLEDIR
"/Resources/zh_TW.lproj/cups.strings", 0))
1445 else if (strstr(locale
, "_") != NULL
|| strstr(locale
, "-") != NULL
)
1448 * Drop country code, just try language...
1451 strlcpy(baselang
, locale
, sizeof(baselang
));
1452 if (baselang
[3] == '-' || baselang
[3] == '_')
1458 snprintf(filename
, sizeof(filename
),
1459 CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings", locale
);
1460 DEBUG_printf(("1appleMessageLoad: alternate filename=\"%s\"", filename
));
1463 url
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
,
1465 (CFIndex
)strlen(filename
), false);
1468 stream
= CFReadStreamCreateWithFile(kCFAllocatorDefault
, url
);
1472 * Read the property list containing the localization data.
1474 * NOTE: This code currently generates a clang "potential leak"
1475 * warning, but the object is released in _cupsMessageFree().
1478 CFReadStreamOpen(stream
);
1481 plist
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, stream
, 0,
1482 kCFPropertyListImmutable
, NULL
,
1486 CFStringRef msg
= CFErrorCopyDescription(error
);
1489 CFStringGetCString(msg
, filename
, sizeof(filename
),
1490 kCFStringEncodingUTF8
);
1491 DEBUG_printf(("1appleMessageLoad: %s", filename
));
1498 plist
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, stream
, 0,
1499 kCFPropertyListImmutable
, NULL
,
1503 if (plist
&& CFGetTypeID(plist
) != CFDictionaryGetTypeID())
1515 DEBUG_printf(("1appleMessageLoad: url=%p, stream=%p, plist=%p", url
, stream
,
1519 * Create and return an empty array to act as a cache for messages, passing the
1520 * plist as the user data.
1523 return (_cupsMessageNew((void *)plist
));
1525 # endif /* CUPS_BUNDLEDIR */
1526 #endif /* __APPLE__ */
1530 * 'cups_cache_lookup()' - Lookup a language in the cache...
1533 static cups_lang_t
* /* O - Language data or NULL */
1535 const char *name
, /* I - Name of locale */
1536 cups_encoding_t encoding
) /* I - Encoding of locale */
1538 cups_lang_t
*lang
; /* Current language */
1541 DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name
,
1542 encoding
, encoding
== CUPS_AUTO_ENCODING
? "auto" :
1543 lang_encodings
[encoding
]));
1546 * Loop through the cache and return a match if found...
1549 for (lang
= lang_cache
; lang
!= NULL
; lang
= lang
->next
)
1551 DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
1552 "encoding=%d(%s)", (void *)lang
, lang
->language
, lang
->encoding
,
1553 lang_encodings
[lang
->encoding
]));
1555 if (!strcmp(lang
->language
, name
) &&
1556 (encoding
== CUPS_AUTO_ENCODING
|| encoding
== lang
->encoding
))
1560 DEBUG_puts("8cups_cache_lookup: returning match!");
1566 DEBUG_puts("8cups_cache_lookup: returning NULL!");
1573 * 'cups_message_compare()' - Compare two messages.
1576 static int /* O - Result of comparison */
1577 cups_message_compare(
1578 _cups_message_t
*m1
, /* I - First message */
1579 _cups_message_t
*m2
) /* I - Second message */
1581 return (strcmp(m1
->id
, m2
->id
));
1586 * 'cups_message_free()' - Free a message.
1590 cups_message_free(_cups_message_t
*m
) /* I - Message */
1603 * 'cups_message_load()' - Load the message catalog for a language.
1607 cups_message_load(cups_lang_t
*lang
) /* I - Language */
1609 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1610 lang
->strings
= appleMessageLoad(lang
->language
);
1613 char filename
[1024]; /* Filename for language locale file */
1614 _cups_globals_t
*cg
= _cupsGlobals();
1615 /* Pointer to library globals */
1618 snprintf(filename
, sizeof(filename
), "%s/%s/cups_%s.po", cg
->localedir
,
1619 lang
->language
, lang
->language
);
1621 if (strchr(lang
->language
, '_') && access(filename
, 0))
1624 * Country localization not available, look for generic localization...
1627 snprintf(filename
, sizeof(filename
), "%s/%.2s/cups_%.2s.po", cg
->localedir
,
1628 lang
->language
, lang
->language
);
1630 if (access(filename
, 0))
1633 * No generic localization, so use POSIX...
1636 DEBUG_printf(("4cups_message_load: access(\"%s\", 0): %s", filename
,
1639 snprintf(filename
, sizeof(filename
), "%s/C/cups_C.po", cg
->localedir
);
1644 * Read the strings from the file...
1647 lang
->strings
= _cupsMessageLoad(filename
, 1);
1648 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
1653 * 'cups_unquote()' - Unquote characters in strings...
1657 cups_unquote(char *d
, /* O - Unquoted string */
1658 const char *s
) /* I - Original string */
1671 *d
= *d
* 8 + *s
- '0';