4 * I18N/language support for CUPS.
6 * Copyright 2007-2012 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 * _cupsAppleLanguage() - Get the Apple language identifier associated with
21 * _cupsEncodingName() - Return the character encoding name string for the
22 * given encoding enumeration.
23 * cupsLangDefault() - Return the default language.
24 * cupsLangEncoding() - Return the character encoding (us-ascii, etc.)
25 * for the given language.
26 * cupsLangFlush() - Flush all language data out of the cache.
27 * cupsLangFree() - Free language data.
28 * cupsLangGet() - Get a language.
29 * _cupsLangString() - Get a message string.
30 * _cupsMessageFree() - Free a messages array.
31 * _cupsMessageLoad() - Load a .po file into a messages array.
32 * _cupsMessageLookup() - Lookup a message string.
33 * _cupsMessageNew() - Make a new message catalog array.
34 * appleLangDefault() - Get the default locale string.
35 * appleMessageLoad() - Load a message catalog from a localizable bundle.
36 * cups_cache_lookup() - Lookup a language in the cache...
37 * cups_message_compare() - Compare two messages.
38 * cups_message_free() - Free a message.
39 * cups_message_load() - Load the message catalog for a language.
40 * cups_unquote() - Unquote characters in strings...
44 * Include necessary headers...
47 #include "cups-private.h"
48 #ifdef HAVE_LANGINFO_H
49 # include <langinfo.h>
50 #endif /* HAVE_LANGINFO_H */
56 #ifdef HAVE_COREFOUNDATION_H
57 # include <CoreFoundation/CoreFoundation.h>
58 #endif /* HAVE_COREFOUNDATION_H */
65 static _cups_mutex_t lang_mutex
= _CUPS_MUTEX_INITIALIZER
;
66 /* Mutex to control access to cache */
67 static cups_lang_t
*lang_cache
= NULL
;
68 /* Language string cache */
69 static const char * const lang_encodings
[] =
70 { /* Encoding strings */
71 "us-ascii", "iso-8859-1",
72 "iso-8859-2", "iso-8859-3",
73 "iso-8859-4", "iso-8859-5",
74 "iso-8859-6", "iso-8859-7",
75 "iso-8859-8", "iso-8859-9",
76 "iso-8859-10", "utf-8",
77 "iso-8859-13", "iso-8859-14",
78 "iso-8859-15", "cp874",
84 "koi8-u", "iso-8859-11",
100 "unknown", "unknown",
101 "unknown", "unknown",
102 "unknown", "unknown",
106 "unknown", "unknown",
107 "unknown", "unknown",
108 "unknown", "unknown",
109 "unknown", "unknown",
110 "unknown", "unknown",
111 "unknown", "unknown",
112 "unknown", "unknown",
113 "unknown", "unknown",
114 "unknown", "unknown",
115 "unknown", "unknown",
116 "unknown", "unknown",
117 "unknown", "unknown",
118 "unknown", "unknown",
119 "unknown", "unknown",
120 "unknown", "unknown",
121 "unknown", "unknown",
122 "unknown", "unknown",
123 "unknown", "unknown",
124 "unknown", "unknown",
125 "unknown", "unknown",
126 "unknown", "unknown",
127 "unknown", "unknown",
128 "unknown", "unknown",
129 "unknown", "unknown",
130 "unknown", "unknown",
131 "unknown", "unknown",
132 "unknown", "unknown",
133 "unknown", "unknown",
134 "unknown", "unknown",
143 const char * const language
; /* Language ID */
144 const char * const locale
; /* Locale ID */
145 } _apple_language_locale_t
;
147 static const _apple_language_locale_t apple_language_locale
[] =
148 { /* Locale to language ID LUT */
151 { "zh-Hans", "zh_CN" },
152 { "zh-Hant", "zh_TW" }
154 #endif /* __APPLE__ */
163 static const char *appleLangDefault(void);
164 # ifdef CUPS_BUNDLEDIR
165 # ifndef CF_RETURNS_RETAINED
166 # if __has_feature(attribute_cf_returns_retained)
167 # define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
169 # define CF_RETURNS_RETAINED
170 # endif /* __has_feature(attribute_cf_returns_retained) */
171 # endif /* !CF_RETURNED_RETAINED */
172 static cups_array_t
*appleMessageLoad(const char *locale
)
174 # endif /* CUPS_BUNDLEDIR */
175 #endif /* __APPLE__ */
176 static cups_lang_t
*cups_cache_lookup(const char *name
,
177 cups_encoding_t encoding
);
178 static int cups_message_compare(_cups_message_t
*m1
,
179 _cups_message_t
*m2
);
180 static void cups_message_free(_cups_message_t
*m
);
181 static void cups_message_load(cups_lang_t
*lang
);
182 static void cups_unquote(char *d
, const char *s
);
187 * '_cupsAppleLanguage()' - Get the Apple language identifier associated with a
191 const char * /* O - Language ID */
192 _cupsAppleLanguage(const char *locale
, /* I - Locale ID */
193 char *language
,/* I - Language ID buffer */
194 size_t langsize
) /* I - Size of language ID buffer */
196 int i
; /* Looping var */
197 CFStringRef localeid
, /* CF locale identifier */
198 langid
; /* CF language identifier */
202 * Copy the locale name and convert, as needed, to the Apple-specific
203 * locale identifier...
206 switch (strlen(locale
))
213 strlcpy(language
, "en", langsize
);
217 strlcpy(language
, locale
, langsize
);
221 strlcpy(language
, locale
, langsize
);
223 if (language
[2] == '-')
226 * Convert ll-cc to ll_CC...
230 language
[3] = toupper(language
[3] & 255);
231 language
[4] = toupper(language
[4] & 255);
237 i
< (int)(sizeof(apple_language_locale
) /
238 sizeof(apple_language_locale
[0]));
240 if (!strcmp(locale
, apple_language_locale
[i
].locale
))
242 strlcpy(language
, apple_language_locale
[i
].language
, sizeof(language
));
247 * Attempt to map the locale ID to a language ID...
250 if ((localeid
= CFStringCreateWithCString(kCFAllocatorDefault
, language
,
251 kCFStringEncodingASCII
)) != NULL
)
253 if ((langid
= CFLocaleCreateCanonicalLanguageIdentifierFromString(
254 kCFAllocatorDefault
, localeid
)) != NULL
)
256 CFStringGetCString(langid
, language
, langsize
, kCFStringEncodingASCII
);
264 * Return what we got...
269 #endif /* __APPLE__ */
273 * '_cupsEncodingName()' - Return the character encoding name string
274 * for the given encoding enumeration.
277 const char * /* O - Character encoding */
279 cups_encoding_t encoding
) /* I - Encoding value */
282 encoding
>= (sizeof(lang_encodings
) / sizeof(const char *)))
284 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = out of range (\"%s\")",
285 encoding
, lang_encodings
[0]));
286 return (lang_encodings
[0]);
290 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = \"%s\"",
291 encoding
, lang_encodings
[encoding
]));
292 return (lang_encodings
[encoding
]);
298 * 'cupsLangDefault()' - Return the default language.
301 cups_lang_t
* /* O - Language data */
302 cupsLangDefault(void)
304 return (cupsLangGet(NULL
));
309 * 'cupsLangEncoding()' - Return the character encoding (us-ascii, etc.)
310 * for the given language.
313 const char * /* O - Character encoding */
314 cupsLangEncoding(cups_lang_t
*lang
) /* I - Language data */
317 return ((char*)lang_encodings
[0]);
319 return ((char*)lang_encodings
[lang
->encoding
]);
324 * 'cupsLangFlush()' - Flush all language data out of the cache.
330 cups_lang_t
*lang
, /* Current language */
331 *next
; /* Next language */
335 * Free all languages in the cache...
338 _cupsMutexLock(&lang_mutex
);
340 for (lang
= lang_cache
; lang
!= NULL
; lang
= next
)
343 * Free all messages...
346 _cupsMessageFree(lang
->strings
);
349 * Then free the language structure itself...
358 _cupsMutexUnlock(&lang_mutex
);
363 * 'cupsLangFree()' - Free language data.
365 * This does not actually free anything; use @link cupsLangFlush@ for that.
369 cupsLangFree(cups_lang_t
*lang
) /* I - Language to free */
371 _cupsMutexLock(&lang_mutex
);
373 if (lang
!= NULL
&& lang
->used
> 0)
376 _cupsMutexUnlock(&lang_mutex
);
381 * 'cupsLangGet()' - Get a language.
384 cups_lang_t
* /* O - Language data */
385 cupsLangGet(const char *language
) /* I - Language or locale */
387 int i
; /* Looping var */
389 char locale
[255]; /* Copy of locale name */
390 #endif /* !__APPLE__ */
391 char langname
[16], /* Requested language name */
392 country
[16], /* Country code */
393 charset
[16], /* Character set */
394 *csptr
, /* Pointer to CODESET string */
395 *ptr
, /* Pointer into language/charset */
396 real
[48]; /* Real language name */
397 cups_encoding_t encoding
; /* Encoding to use */
398 cups_lang_t
*lang
; /* Current language... */
399 static const char * const locale_encodings
[] =
400 { /* Locale charset names */
401 "ASCII", "ISO88591", "ISO88592", "ISO88593",
402 "ISO88594", "ISO88595", "ISO88596", "ISO88597",
403 "ISO88598", "ISO88599", "ISO885910", "UTF8",
404 "ISO885913", "ISO885914", "ISO885915", "CP874",
405 "CP1250", "CP1251", "CP1252", "CP1253",
406 "CP1254", "CP1255", "CP1256", "CP1257",
407 "CP1258", "KOI8R", "KOI8U", "ISO885911",
408 "ISO885916", "MACROMAN", "", "",
419 "CP932", "CP936", "CP949", "CP950",
420 "CP1361", "", "", "",
437 "EUCCN", "EUCJP", "EUCKR", "EUCTW",
442 DEBUG_printf(("2cupsLangGet(language=\"%s\")", language
));
446 * Set the character set to UTF-8...
449 strlcpy(charset
, "UTF8", sizeof(charset
));
452 * Apple's setlocale doesn't give us the user's localization
453 * preference so we have to look it up this way...
458 if (!getenv("SOFTWARE") || (language
= getenv("LANG")) == NULL
)
459 language
= appleLangDefault();
461 DEBUG_printf(("4cupsLangGet: language=\"%s\"", language
));
466 * Set the charset to "unknown"...
472 * Use setlocale() to determine the currently set locale, and then
473 * fallback to environment variables to avoid setting the locale,
474 * since setlocale() is not thread-safe!
480 * First see if the locale has been set; if it is still "C" or
481 * "POSIX", use the environment to get the default...
485 ptr
= setlocale(LC_MESSAGES
, NULL
);
487 ptr
= setlocale(LC_ALL
, NULL
);
488 # endif /* LC_MESSAGES */
490 DEBUG_printf(("4cupsLangGet: current locale is \"%s\"", ptr
));
492 if (!ptr
|| !strcmp(ptr
, "C") || !strcmp(ptr
, "POSIX"))
495 * Get the character set from the LC_CTYPE locale setting...
498 if ((ptr
= getenv("LC_CTYPE")) == NULL
)
499 if ((ptr
= getenv("LC_ALL")) == NULL
)
500 if ((ptr
= getenv("LANG")) == NULL
)
503 if ((csptr
= strchr(ptr
, '.')) != NULL
)
506 * Extract the character set from the environment...
509 for (ptr
= charset
, csptr
++; *csptr
; csptr
++)
510 if (ptr
< (charset
+ sizeof(charset
) - 1) && _cups_isalnum(*csptr
))
517 * Get the locale for messages from the LC_MESSAGES locale setting...
520 if ((ptr
= getenv("LC_MESSAGES")) == NULL
)
521 if ((ptr
= getenv("LC_ALL")) == NULL
)
522 if ((ptr
= getenv("LANG")) == NULL
)
528 strlcpy(locale
, ptr
, sizeof(locale
));
532 * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
535 if (!strncmp(locale
, "nb", 2))
538 DEBUG_printf(("4cupsLangGet: new language value is \"%s\"", language
));
541 #endif /* __APPLE__ */
544 * If "language" is NULL at this point, then chances are we are using
545 * a language that is not installed for the base OS.
551 * Switch to the POSIX ("C") locale...
559 * On systems that support the nl_langinfo(CODESET) call, use
560 * this value as the character set...
563 if (!charset
[0] && (csptr
= nl_langinfo(CODESET
)) != NULL
)
566 * Copy all of the letters and numbers in the CODESET string...
569 for (ptr
= charset
; *csptr
; csptr
++)
570 if (_cups_isalnum(*csptr
) && ptr
< (charset
+ sizeof(charset
) - 1))
575 DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via "
576 "nl_langinfo(CODESET)...", charset
));
581 * If we don't have a character set by now, default to UTF-8...
585 strlcpy(charset
, "UTF8", sizeof(charset
));
588 * Parse the language string passed in to a locale string. "C" is the
589 * standard POSIX locale and is copied unchanged. Otherwise the
590 * language string is converted from ll-cc[.charset] (language-country)
591 * to ll_CC[.CHARSET] to match the file naming convention used by all
592 * POSIX-compliant operating systems. Invalid language names are mapped
593 * to the POSIX locale.
598 if (language
== NULL
|| !language
[0] ||
599 !strcmp(language
, "POSIX"))
600 strlcpy(langname
, "C", sizeof(langname
));
604 * Copy the parts of the locale string over safely...
607 for (ptr
= langname
; *language
; language
++)
608 if (*language
== '_' || *language
== '-' || *language
== '.')
610 else if (ptr
< (langname
+ sizeof(langname
) - 1))
611 *ptr
++ = tolower(*language
& 255);
615 if (*language
== '_' || *language
== '-')
618 * Copy the country code...
621 for (language
++, ptr
= country
; *language
; language
++)
622 if (*language
== '.')
624 else if (ptr
< (country
+ sizeof(country
) - 1))
625 *ptr
++ = toupper(*language
& 255);
630 if (*language
== '.' && !charset
[0])
633 * Copy the encoding...
636 for (language
++, ptr
= charset
; *language
; language
++)
637 if (_cups_isalnum(*language
) && ptr
< (charset
+ sizeof(charset
) - 1))
638 *ptr
++ = toupper(*language
& 255);
644 * Force a POSIX locale for an invalid language name...
647 if (strlen(langname
) != 2)
649 strlcpy(langname
, "C", sizeof(langname
));
655 DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"",
656 langname
, country
, charset
));
659 * Figure out the desired encoding...
662 encoding
= CUPS_AUTO_ENCODING
;
667 i
< (int)(sizeof(locale_encodings
) / sizeof(locale_encodings
[0]));
669 if (!_cups_strcasecmp(charset
, locale_encodings
[i
]))
671 encoding
= (cups_encoding_t
)i
;
675 if (encoding
== CUPS_AUTO_ENCODING
)
678 * Map alternate names for various character sets...
681 if (!_cups_strcasecmp(charset
, "iso-2022-jp") ||
682 !_cups_strcasecmp(charset
, "sjis"))
683 encoding
= CUPS_WINDOWS_932
;
684 else if (!_cups_strcasecmp(charset
, "iso-2022-cn"))
685 encoding
= CUPS_WINDOWS_936
;
686 else if (!_cups_strcasecmp(charset
, "iso-2022-kr"))
687 encoding
= CUPS_WINDOWS_949
;
688 else if (!_cups_strcasecmp(charset
, "big5"))
689 encoding
= CUPS_WINDOWS_950
;
693 DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding
,
694 encoding
== CUPS_AUTO_ENCODING
? "auto" :
695 lang_encodings
[encoding
]));
698 * See if we already have this language/country loaded...
702 snprintf(real
, sizeof(real
), "%s_%s", langname
, country
);
704 strlcpy(real
, langname
, sizeof(real
));
706 _cupsMutexLock(&lang_mutex
);
708 if ((lang
= cups_cache_lookup(real
, encoding
)) != NULL
)
710 _cupsMutexUnlock(&lang_mutex
);
712 DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real
));
718 * See if there is a free language available; if so, use that
722 for (lang
= lang_cache
; lang
!= NULL
; lang
= lang
->next
)
729 * Allocate memory for the language and add it to the cache.
732 if ((lang
= calloc(sizeof(cups_lang_t
), 1)) == NULL
)
734 _cupsMutexUnlock(&lang_mutex
);
739 lang
->next
= lang_cache
;
745 * Free all old strings as needed...
748 _cupsMessageFree(lang
->strings
);
749 lang
->strings
= NULL
;
753 * Then assign the language and encoding fields...
757 strlcpy(lang
->language
, real
, sizeof(lang
->language
));
759 if (encoding
!= CUPS_AUTO_ENCODING
)
760 lang
->encoding
= encoding
;
762 lang
->encoding
= CUPS_UTF8
;
768 _cupsMutexUnlock(&lang_mutex
);
775 * '_cupsLangString()' - Get a message string.
777 * The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to
778 * convert the string to the language encoding.
781 const char * /* O - Localized message */
782 _cupsLangString(cups_lang_t
*lang
, /* I - Language */
783 const char *message
) /* I - Message */
785 const char *s
; /* Localized message */
788 * Range check input...
791 if (!lang
|| !message
|| !*message
)
794 _cupsMutexLock(&lang_mutex
);
797 * Load the message catalog if needed...
801 cups_message_load(lang
);
803 s
= _cupsMessageLookup(lang
->strings
, message
);
805 _cupsMutexUnlock(&lang_mutex
);
812 * '_cupsMessageFree()' - Free a messages array.
816 _cupsMessageFree(cups_array_t
*a
) /* I - Message array */
818 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
820 * Release the cups.strings dictionary as needed...
823 if (cupsArrayUserData(a
))
824 CFRelease((CFDictionaryRef
)cupsArrayUserData(a
));
825 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
836 * '_cupsMessageLoad()' - Load a .po file into a messages array.
839 cups_array_t
* /* O - New message array */
840 _cupsMessageLoad(const char *filename
, /* I - Message catalog to load */
841 int unquote
) /* I - Unescape \foo in strings? */
843 cups_file_t
*fp
; /* Message file */
844 cups_array_t
*a
; /* Message array */
845 _cups_message_t
*m
; /* Current message */
846 char s
[4096], /* String buffer */
847 *ptr
, /* Pointer into buffer */
848 *temp
; /* New string */
849 int length
; /* Length of combined strings */
850 size_t ptrlen
; /* Length of string */
853 DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename
));
856 * Create an array to hold the messages...
859 if ((a
= _cupsMessageNew(NULL
)) == NULL
)
861 DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
866 * Open the message catalog file...
869 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
871 DEBUG_printf(("5_cupsMessageLoad: Unable to open file: %s",
877 * Read messages from the catalog file until EOF...
879 * The format is the GNU gettext .po format, which is fairly simple:
882 * msgstr "localized text"
884 * The ID and localized text can span multiple lines using the form:
889 * "localized text spanning "
895 while (cupsFileGets(fp
, s
, sizeof(s
)) != NULL
)
898 * Skip blank and comment lines...
901 if (s
[0] == '#' || !s
[0])
905 * Strip the trailing quote...
908 if ((ptr
= strrchr(s
, '\"')) == NULL
)
914 * Find start of value...
917 if ((ptr
= strchr(s
, '\"')) == NULL
)
923 * Unquote the text...
927 cups_unquote(ptr
, ptr
);
930 * Create or add to a message...
933 if (!strncmp(s
, "msgid", 5))
936 * Add previous message as needed...
941 if (m
->str
&& m
->str
[0])
948 * Translation is empty, don't add it... (STR #4033)
959 * Create a new message with the given msgid string...
962 if ((m
= (_cups_message_t
*)calloc(1, sizeof(_cups_message_t
))) == NULL
)
968 if ((m
->id
= strdup(ptr
)) == NULL
)
975 else if (s
[0] == '\"' && m
)
978 * Append to current string...
981 length
= (int)strlen(m
->str
? m
->str
: m
->id
);
982 ptrlen
= strlen(ptr
);
984 if ((temp
= realloc(m
->str
? m
->str
: m
->id
,
985 length
+ ptrlen
+ 1)) == NULL
)
999 * Copy the new portion to the end of the msgstr string - safe
1000 * to use memcpy because the buffer is allocated to the correct
1006 memcpy(m
->str
+ length
, ptr
, ptrlen
+ 1);
1011 * Copy the new portion to the end of the msgid string - safe
1012 * to use memcpy because the buffer is allocated to the correct
1018 memcpy(m
->id
+ length
, ptr
, ptrlen
+ 1);
1021 else if (!strncmp(s
, "msgstr", 6) && m
)
1027 if ((m
->str
= strdup(ptr
)) == NULL
)
1039 * Add the last message string to the array as needed...
1044 if (m
->str
&& m
->str
[0])
1051 * Translation is empty, don't add it... (STR #4033)
1062 * Close the message catalog file and return the new array...
1067 DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
1068 cupsArrayCount(a
)));
1075 * '_cupsMessageLookup()' - Lookup a message string.
1078 const char * /* O - Localized message */
1079 _cupsMessageLookup(cups_array_t
*a
, /* I - Message array */
1080 const char *m
) /* I - Message */
1082 _cups_message_t key
, /* Search key */
1083 *match
; /* Matching message */
1087 * Lookup the message string; if it doesn't exist in the catalog,
1088 * then return the message that was passed to us...
1092 match
= (_cups_message_t
*)cupsArrayFind(a
, &key
);
1094 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1095 if (!match
&& cupsArrayUserData(a
))
1098 * Try looking the string up in the cups.strings dictionary...
1101 CFDictionaryRef dict
; /* cups.strings dictionary */
1102 CFStringRef cfm
, /* Message as a CF string */
1103 cfstr
; /* Localized text as a CF string */
1105 dict
= (CFDictionaryRef
)cupsArrayUserData(a
);
1106 cfm
= CFStringCreateWithCString(kCFAllocatorDefault
, m
,
1107 kCFStringEncodingUTF8
);
1108 match
= calloc(1, sizeof(_cups_message_t
));
1109 match
->id
= strdup(m
);
1110 cfstr
= cfm
? CFDictionaryGetValue(dict
, cfm
) : NULL
;
1114 char buffer
[1024]; /* Message buffer */
1116 CFStringGetCString(cfstr
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
);
1117 match
->str
= strdup(buffer
);
1119 DEBUG_printf(("1_cupsMessageLookup: Found \"%s\" as \"%s\"...",
1124 match
->str
= strdup(m
);
1126 DEBUG_printf(("1_cupsMessageLookup: Did not find \"%s\"...", m
));
1129 cupsArrayAdd(a
, match
);
1134 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
1136 if (match
&& match
->str
)
1137 return (match
->str
);
1144 * '_cupsMessageNew()' - Make a new message catalog array.
1147 cups_array_t
* /* O - Array */
1148 _cupsMessageNew(void *context
) /* I - User data */
1150 return (cupsArrayNew3((cups_array_func_t
)cups_message_compare
, context
,
1151 (cups_ahash_func_t
)NULL
, 0,
1152 (cups_acopy_func_t
)NULL
,
1153 (cups_afree_func_t
)cups_message_free
));
1159 * 'appleLangDefault()' - Get the default locale string.
1162 static const char * /* O - Locale string */
1163 appleLangDefault(void)
1165 int i
; /* Looping var */
1166 CFBundleRef bundle
; /* Main bundle (if any) */
1167 CFArrayRef bundleList
; /* List of localizations in bundle */
1168 CFPropertyListRef localizationList
;
1169 /* List of localization data */
1170 CFStringRef languageName
; /* Current name */
1171 CFStringRef localeName
; /* Canonical from of name */
1172 char *lang
; /* LANG environment variable */
1173 _cups_globals_t
*cg
= _cupsGlobals();
1174 /* Pointer to library globals */
1177 DEBUG_puts("2appleLangDefault()");
1180 * Only do the lookup and translation the first time.
1183 if (!cg
->language
[0])
1185 if (getenv("SOFTWARE") != NULL
&& (lang
= getenv("LANG")) != NULL
)
1187 DEBUG_printf(("3appleLangDefault: Using LANG=%s", lang
));
1188 strlcpy(cg
->language
, lang
, sizeof(cg
->language
));
1189 return (cg
->language
);
1191 else if ((bundle
= CFBundleGetMainBundle()) != NULL
&&
1192 (bundleList
= CFBundleCopyBundleLocalizations(bundle
)) != NULL
)
1194 DEBUG_puts("3appleLangDefault: Getting localizationList from bundle.");
1197 CFBundleCopyPreferredLocalizationsFromArray(bundleList
);
1199 CFRelease(bundleList
);
1203 DEBUG_puts("3appleLangDefault: Getting localizationList from preferences.");
1206 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1207 kCFPreferencesCurrentApplication
);
1210 if (localizationList
)
1214 if (CFGetTypeID(localizationList
) == CFArrayGetTypeID())
1215 DEBUG_printf(("3appleLangDefault: Got localizationList, %d entries.",
1216 (int)CFArrayGetCount(localizationList
)));
1218 DEBUG_puts("3appleLangDefault: Got localizationList but not an array.");
1221 if (CFGetTypeID(localizationList
) == CFArrayGetTypeID() &&
1222 CFArrayGetCount(localizationList
) > 0)
1224 languageName
= CFArrayGetValueAtIndex(localizationList
, 0);
1227 CFGetTypeID(languageName
) == CFStringGetTypeID())
1229 localeName
= CFLocaleCreateCanonicalLocaleIdentifierFromString(
1230 kCFAllocatorDefault
, languageName
);
1234 CFStringGetCString(localeName
, cg
->language
, sizeof(cg
->language
),
1235 kCFStringEncodingASCII
);
1236 CFRelease(localeName
);
1238 DEBUG_printf(("3appleLangDefault: cg->language=\"%s\"",
1242 * Map new language identifiers to locales...
1246 i
< (int)(sizeof(apple_language_locale
) /
1247 sizeof(apple_language_locale
[0]));
1250 if (!strcmp(cg
->language
, apple_language_locale
[i
].language
))
1252 DEBUG_printf(("3appleLangDefault: mapping \"%s\" to \"%s\"...",
1253 cg
->language
, apple_language_locale
[i
].locale
));
1254 strlcpy(cg
->language
, apple_language_locale
[i
].locale
,
1255 sizeof(cg
->language
));
1261 * Convert language subtag into region subtag...
1264 if (cg
->language
[2] == '-')
1265 cg
->language
[2] = '_';
1267 if (!strchr(cg
->language
, '.'))
1268 strlcat(cg
->language
, ".UTF-8", sizeof(cg
->language
));
1271 DEBUG_puts("3appleLangDefault: Unable to get localeName.");
1275 CFRelease(localizationList
);
1279 * If we didn't find the language, default to en_US...
1282 if (!cg
->language
[0])
1284 DEBUG_puts("3appleLangDefault: Defaulting to en_US.");
1285 strlcpy(cg
->language
, "en_US.UTF-8", sizeof(cg
->language
));
1290 * Return the cached locale...
1293 return (cg
->language
);
1297 # ifdef CUPS_BUNDLEDIR
1299 * 'appleMessageLoad()' - Load a message catalog from a localizable bundle.
1302 static cups_array_t
* /* O - Message catalog */
1303 appleMessageLoad(const char *locale
) /* I - Locale ID */
1305 char filename
[1024], /* Path to cups.strings file */
1306 applelang
[256]; /* Apple language ID */
1307 CFURLRef url
; /* URL to cups.strings file */
1308 CFReadStreamRef stream
= NULL
; /* File stream */
1309 CFPropertyListRef plist
= NULL
; /* Localization file */
1311 CFErrorRef error
= NULL
; /* Error when opening file */
1315 DEBUG_printf(("appleMessageLoad(locale=\"%s\")", locale
));
1318 * Load the cups.strings file...
1321 snprintf(filename
, sizeof(filename
),
1322 CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings",
1323 _cupsAppleLanguage(locale
, applelang
, sizeof(applelang
)));
1324 DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename
));
1326 if (access(filename
, 0))
1329 * Try alternate lproj directory names...
1332 if (!strncmp(locale
, "en", 2))
1334 else if (!strncmp(locale
, "nb", 2) || !strncmp(locale
, "nl", 2))
1336 else if (!strncmp(locale
, "fr", 2))
1338 else if (!strncmp(locale
, "de", 2))
1340 else if (!strncmp(locale
, "it", 2))
1342 else if (!strncmp(locale
, "ja", 2))
1343 locale
= "Japanese";
1344 else if (!strncmp(locale
, "es", 2))
1347 snprintf(filename
, sizeof(filename
),
1348 CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings", locale
);
1349 DEBUG_printf(("1appleMessageLoad: alternate filename=\"%s\"", filename
));
1352 url
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
,
1354 strlen(filename
), false);
1357 stream
= CFReadStreamCreateWithFile(kCFAllocatorDefault
, url
);
1361 * Read the property list containing the localization data.
1363 * NOTE: This code currently generates a clang "potential leak"
1364 * warning, but the object is released in _cupsMessageFree().
1367 CFReadStreamOpen(stream
);
1370 plist
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, stream
, 0,
1371 kCFPropertyListImmutable
, NULL
,
1375 CFStringRef msg
= CFErrorCopyDescription(error
);
1378 CFStringGetCString(msg
, filename
, sizeof(filename
),
1379 kCFStringEncodingUTF8
);
1380 DEBUG_printf(("1appleMessageLoad: %s", filename
));
1387 plist
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, stream
, 0,
1388 kCFPropertyListImmutable
, NULL
,
1392 if (plist
&& CFGetTypeID(plist
) != CFDictionaryGetTypeID())
1404 DEBUG_printf(("1appleMessageLoad: url=%p, stream=%p, plist=%p", url
, stream
,
1408 * Create and return an empty array to act as a cache for messages, passing the
1409 * plist as the user data.
1412 return (_cupsMessageNew((void *)plist
));
1414 # endif /* CUPS_BUNDLEDIR */
1415 #endif /* __APPLE__ */
1419 * 'cups_cache_lookup()' - Lookup a language in the cache...
1422 static cups_lang_t
* /* O - Language data or NULL */
1424 const char *name
, /* I - Name of locale */
1425 cups_encoding_t encoding
) /* I - Encoding of locale */
1427 cups_lang_t
*lang
; /* Current language */
1430 DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name
,
1431 encoding
, encoding
== CUPS_AUTO_ENCODING
? "auto" :
1432 lang_encodings
[encoding
]));
1435 * Loop through the cache and return a match if found...
1438 for (lang
= lang_cache
; lang
!= NULL
; lang
= lang
->next
)
1440 DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
1441 "encoding=%d(%s)", lang
, lang
->language
, lang
->encoding
,
1442 lang_encodings
[lang
->encoding
]));
1444 if (!strcmp(lang
->language
, name
) &&
1445 (encoding
== CUPS_AUTO_ENCODING
|| encoding
== lang
->encoding
))
1449 DEBUG_puts("8cups_cache_lookup: returning match!");
1455 DEBUG_puts("8cups_cache_lookup: returning NULL!");
1462 * 'cups_message_compare()' - Compare two messages.
1465 static int /* O - Result of comparison */
1466 cups_message_compare(
1467 _cups_message_t
*m1
, /* I - First message */
1468 _cups_message_t
*m2
) /* I - Second message */
1470 return (strcmp(m1
->id
, m2
->id
));
1475 * 'cups_message_free()' - Free a message.
1479 cups_message_free(_cups_message_t
*m
) /* I - Message */
1492 * 'cups_message_load()' - Load the message catalog for a language.
1496 cups_message_load(cups_lang_t
*lang
) /* I - Language */
1498 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1499 lang
->strings
= appleMessageLoad(lang
->language
);
1502 char filename
[1024]; /* Filename for language locale file */
1503 _cups_globals_t
*cg
= _cupsGlobals();
1504 /* Pointer to library globals */
1507 snprintf(filename
, sizeof(filename
), "%s/%s/cups_%s.po", cg
->localedir
,
1508 lang
->language
, lang
->language
);
1510 if (strchr(lang
->language
, '_') && access(filename
, 0))
1513 * Country localization not available, look for generic localization...
1516 snprintf(filename
, sizeof(filename
), "%s/%.2s/cups_%.2s.po", cg
->localedir
,
1517 lang
->language
, lang
->language
);
1519 if (access(filename
, 0))
1522 * No generic localization, so use POSIX...
1525 DEBUG_printf(("4cups_message_load: access(\"%s\", 0): %s", filename
,
1528 snprintf(filename
, sizeof(filename
), "%s/C/cups_C.po", cg
->localedir
);
1533 * Read the strings from the file...
1536 lang
->strings
= _cupsMessageLoad(filename
, 1);
1537 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
1542 * 'cups_unquote()' - Unquote characters in strings...
1546 cups_unquote(char *d
, /* O - Unquoted string */
1547 const char *s
) /* I - Original string */
1560 *d
= *d
* 8 + *s
- '0';