2 * "$Id: language.c 7558 2008-05-12 23:46:44Z mike $"
4 * I18N/language support for CUPS.
6 * Copyright 2007-2011 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.) for
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 * appleLangDefault() - Get the default locale string.
34 * appleMessageLoad() - Load a message catalog from a localizable bundle.
35 * cups_cache_lookup() - Lookup a language in the cache...
36 * cups_message_compare() - Compare two messages.
37 * cups_message_free() - Free a message.
38 * cups_unquote() - Unquote characters in strings...
42 * Include necessary headers...
45 #include "cups-private.h"
46 #ifdef HAVE_LANGINFO_H
47 # include <langinfo.h>
48 #endif /* HAVE_LANGINFO_H */
54 #ifdef HAVE_COREFOUNDATION_H
55 # include <CoreFoundation/CoreFoundation.h>
56 #endif /* HAVE_COREFOUNDATION_H */
63 static _cups_mutex_t lang_mutex
= _CUPS_MUTEX_INITIALIZER
;
64 /* Mutex to control access to cache */
65 static cups_lang_t
*lang_cache
= NULL
;
66 /* Language string cache */
67 static const char * const lang_encodings
[] =
68 { /* Encoding strings */
69 "us-ascii", "iso-8859-1",
70 "iso-8859-2", "iso-8859-3",
71 "iso-8859-4", "iso-8859-5",
72 "iso-8859-6", "iso-8859-7",
73 "iso-8859-8", "iso-8859-9",
74 "iso-8859-10", "utf-8",
75 "iso-8859-13", "iso-8859-14",
76 "iso-8859-15", "cp874",
82 "koi8-u", "iso-8859-11",
83 "iso-8859-16", "mac-roman",
100 "unknown", "unknown",
104 "unknown", "unknown",
105 "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",
141 const char * const language
; /* Language ID */
142 const char * const locale
; /* Locale ID */
143 } _apple_language_locale_t
;
145 static const _apple_language_locale_t apple_language_locale
[] =
146 { /* Locale to language ID LUT */
149 { "zh-Hans", "zh_CN" },
150 { "zh-Hant", "zh_TW" }
152 #endif /* __APPLE__ */
161 static const char *appleLangDefault(void);
162 # ifdef CUPS_BUNDLEDIR
163 # ifndef CF_RETURNS_RETAINED
164 # if __has_feature(attribute_cf_returns_retained)
165 # define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
167 # define CF_RETURNS_RETAINED
168 # endif /* __has_feature(attribute_cf_returns_retained) */
169 # endif /* !CF_RETURNED_RETAINED */
170 static cups_array_t
*appleMessageLoad(const char *locale
)
172 # endif /* CUPS_BUNDLEDIR */
173 #endif /* __APPLE__ */
174 static cups_lang_t
*cups_cache_lookup(const char *name
,
175 cups_encoding_t encoding
);
176 static int cups_message_compare(_cups_message_t
*m1
,
177 _cups_message_t
*m2
);
178 static void cups_message_free(_cups_message_t
*m
);
179 static void cups_unquote(char *d
, const char *s
);
184 * '_cupsAppleLanguage()' - Get the Apple language identifier associated with a
188 const char * /* O - Language ID */
189 _cupsAppleLanguage(const char *locale
, /* I - Locale ID */
190 char *language
,/* I - Language ID buffer */
191 size_t langsize
) /* I - Size of language ID buffer */
193 int i
; /* Looping var */
194 CFStringRef localeid
, /* CF locale identifier */
195 langid
; /* CF language identifier */
199 * Copy the locale name and convert, as needed, to the Apple-specific
200 * locale identifier...
203 switch (strlen(locale
))
210 strlcpy(language
, "en", langsize
);
214 strlcpy(language
, locale
, langsize
);
218 strlcpy(language
, locale
, langsize
);
220 if (language
[2] == '-')
223 * Convert ll-cc to ll_CC...
227 language
[3] = toupper(language
[3] & 255);
228 language
[4] = toupper(language
[4] & 255);
234 i
< (int)(sizeof(apple_language_locale
) /
235 sizeof(apple_language_locale
[0]));
237 if (!strcmp(locale
, apple_language_locale
[i
].locale
))
239 strlcpy(language
, apple_language_locale
[i
].language
, sizeof(language
));
244 * Attempt to map the locale ID to a language ID...
247 if ((localeid
= CFStringCreateWithCString(kCFAllocatorDefault
, language
,
248 kCFStringEncodingASCII
)) != NULL
)
250 if ((langid
= CFLocaleCreateCanonicalLanguageIdentifierFromString(
251 kCFAllocatorDefault
, localeid
)) != NULL
)
253 CFStringGetCString(langid
, language
, langsize
, kCFStringEncodingASCII
);
261 * Return what we got...
266 #endif /* __APPLE__ */
270 * '_cupsEncodingName()' - Return the character encoding name string
271 * for the given encoding enumeration.
274 const char * /* O - Character encoding */
276 cups_encoding_t encoding
) /* I - Encoding value */
279 encoding
>= (sizeof(lang_encodings
) / sizeof(const char *)))
281 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = out of range (\"%s\")",
282 encoding
, lang_encodings
[0]));
283 return (lang_encodings
[0]);
287 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = \"%s\"",
288 encoding
, lang_encodings
[encoding
]));
289 return (lang_encodings
[encoding
]);
295 * 'cupsLangDefault()' - Return the default language.
298 cups_lang_t
* /* O - Language data */
299 cupsLangDefault(void)
301 return (cupsLangGet(NULL
));
306 * 'cupsLangEncoding()' - Return the character encoding (us-ascii, etc.)
307 * for the given language.
310 const char * /* O - Character encoding */
311 cupsLangEncoding(cups_lang_t
*lang
) /* I - Language data */
314 return ((char*)lang_encodings
[0]);
316 return ((char*)lang_encodings
[lang
->encoding
]);
321 * 'cupsLangFlush()' - Flush all language data out of the cache.
327 cups_lang_t
*lang
, /* Current language */
328 *next
; /* Next language */
332 * Free all languages in the cache...
335 _cupsMutexLock(&lang_mutex
);
337 for (lang
= lang_cache
; lang
!= NULL
; lang
= next
)
340 * Free all messages...
343 _cupsMessageFree(lang
->strings
);
346 * Then free the language structure itself...
355 _cupsMutexUnlock(&lang_mutex
);
360 * 'cupsLangFree()' - Free language data.
362 * This does not actually free anything; use @link cupsLangFlush@ for that.
366 cupsLangFree(cups_lang_t
*lang
) /* I - Language to free */
368 _cupsMutexLock(&lang_mutex
);
370 if (lang
!= NULL
&& lang
->used
> 0)
373 _cupsMutexUnlock(&lang_mutex
);
378 * 'cupsLangGet()' - Get a language.
381 cups_lang_t
* /* O - Language data */
382 cupsLangGet(const char *language
) /* I - Language or locale */
384 int i
; /* Looping var */
386 char locale
[255]; /* Copy of locale name */
387 #endif /* !__APPLE__ */
388 char langname
[16], /* Requested language name */
389 country
[16], /* Country code */
390 charset
[16], /* Character set */
391 *csptr
, /* Pointer to CODESET string */
392 *ptr
, /* Pointer into language/charset */
393 real
[48]; /* Real language name */
394 cups_encoding_t encoding
; /* Encoding to use */
395 cups_lang_t
*lang
; /* Current language... */
396 #if !defined(__APPLE__) || !defined(CUPS_BUNDLEDIR)
397 char filename
[1024]; /* Filename for language locale file */
398 _cups_globals_t
*cg
= _cupsGlobals();
399 /* Pointer to library globals */
400 #endif /* !__APPLE__ || !CUPS_BUNDLEDIR */
401 static const char * const locale_encodings
[] =
402 { /* Locale charset names */
403 "ASCII", "ISO88591", "ISO88592", "ISO88593",
404 "ISO88594", "ISO88595", "ISO88596", "ISO88597",
405 "ISO88598", "ISO88599", "ISO885910", "UTF8",
406 "ISO885913", "ISO885914", "ISO885915", "CP874",
407 "CP1250", "CP1251", "CP1252", "CP1253",
408 "CP1254", "CP1255", "CP1256", "CP1257",
409 "CP1258", "KOI8R", "KOI8U", "ISO885911",
410 "ISO885916", "MACROMAN", "", "",
421 "CP932", "CP936", "CP949", "CP950",
422 "CP1361", "", "", "",
439 "EUCCN", "EUCJP", "EUCKR", "EUCTW",
444 DEBUG_printf(("2cupsLangGet(language=\"%s\")", language
));
448 * Set the character set to UTF-8...
451 strcpy(charset
, "UTF8");
454 * Apple's setlocale doesn't give us the user's localization
455 * preference so we have to look it up this way...
460 if (!getenv("SOFTWARE") || (language
= getenv("LANG")) == NULL
)
461 language
= appleLangDefault();
463 DEBUG_printf(("4cupsLangGet: language=\"%s\"", language
));
468 * Set the charset to "unknown"...
474 * Use setlocale() to determine the currently set locale, and then
475 * fallback to environment variables to avoid setting the locale,
476 * since setlocale() is not thread-safe!
482 * First see if the locale has been set; if it is still "C" or
483 * "POSIX", use the environment to get the default...
487 ptr
= setlocale(LC_MESSAGES
, NULL
);
489 ptr
= setlocale(LC_ALL
, NULL
);
490 # endif /* LC_MESSAGES */
492 DEBUG_printf(("4cupsLangGet: current locale is \"%s\"", ptr
));
494 if (!ptr
|| !strcmp(ptr
, "C") || !strcmp(ptr
, "POSIX"))
497 * Get the character set from the LC_CTYPE locale setting...
500 if ((ptr
= getenv("LC_CTYPE")) == NULL
)
501 if ((ptr
= getenv("LC_ALL")) == NULL
)
502 if ((ptr
= getenv("LANG")) == NULL
)
505 if ((csptr
= strchr(ptr
, '.')) != NULL
)
508 * Extract the character set from the environment...
511 for (ptr
= charset
, csptr
++; *csptr
; csptr
++)
512 if (ptr
< (charset
+ sizeof(charset
) - 1) && _cups_isalnum(*csptr
))
519 * Get the locale for messages from the LC_MESSAGES locale setting...
522 if ((ptr
= getenv("LC_MESSAGES")) == NULL
)
523 if ((ptr
= getenv("LC_ALL")) == NULL
)
524 if ((ptr
= getenv("LANG")) == NULL
)
530 strlcpy(locale
, ptr
, sizeof(locale
));
534 * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
537 if (!strncmp(locale
, "nb", 2))
540 DEBUG_printf(("4cupsLangGet: new language value is \"%s\"", language
));
543 #endif /* __APPLE__ */
546 * If "language" is NULL at this point, then chances are we are using
547 * a language that is not installed for the base OS.
553 * Switch to the POSIX ("C") locale...
561 * On systems that support the nl_langinfo(CODESET) call, use
562 * this value as the character set...
565 if (!charset
[0] && (csptr
= nl_langinfo(CODESET
)) != NULL
)
568 * Copy all of the letters and numbers in the CODESET string...
571 for (ptr
= charset
; *csptr
; csptr
++)
572 if (_cups_isalnum(*csptr
) && ptr
< (charset
+ sizeof(charset
) - 1))
577 DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via "
578 "nl_langinfo(CODESET)...", charset
));
583 * If we don't have a character set by now, default to UTF-8...
587 strcpy(charset
, "UTF8");
590 * Parse the language string passed in to a locale string. "C" is the
591 * standard POSIX locale and is copied unchanged. Otherwise the
592 * language string is converted from ll-cc[.charset] (language-country)
593 * to ll_CC[.CHARSET] to match the file naming convention used by all
594 * POSIX-compliant operating systems. Invalid language names are mapped
595 * to the POSIX locale.
600 if (language
== NULL
|| !language
[0] ||
601 !strcmp(language
, "POSIX"))
602 strcpy(langname
, "C");
606 * Copy the parts of the locale string over safely...
609 for (ptr
= langname
; *language
; language
++)
610 if (*language
== '_' || *language
== '-' || *language
== '.')
612 else if (ptr
< (langname
+ sizeof(langname
) - 1))
613 *ptr
++ = tolower(*language
& 255);
617 if (*language
== '_' || *language
== '-')
620 * Copy the country code...
623 for (language
++, ptr
= country
; *language
; language
++)
624 if (*language
== '.')
626 else if (ptr
< (country
+ sizeof(country
) - 1))
627 *ptr
++ = toupper(*language
& 255);
632 if (*language
== '.' && !charset
[0])
635 * Copy the encoding...
638 for (language
++, ptr
= charset
; *language
; language
++)
639 if (_cups_isalnum(*language
) && ptr
< (charset
+ sizeof(charset
) - 1))
640 *ptr
++ = toupper(*language
& 255);
646 * Force a POSIX locale for an invalid language name...
649 if (strlen(langname
) != 2)
651 strcpy(langname
, "C");
657 DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"",
658 langname
, country
, charset
));
661 * Figure out the desired encoding...
664 encoding
= CUPS_AUTO_ENCODING
;
669 i
< (int)(sizeof(locale_encodings
) / sizeof(locale_encodings
[0]));
671 if (!strcasecmp(charset
, locale_encodings
[i
]))
673 encoding
= (cups_encoding_t
)i
;
677 if (encoding
== CUPS_AUTO_ENCODING
)
680 * Map alternate names for various character sets...
683 if (!strcasecmp(charset
, "iso-2022-jp") ||
684 !strcasecmp(charset
, "sjis"))
685 encoding
= CUPS_WINDOWS_932
;
686 else if (!strcasecmp(charset
, "iso-2022-cn"))
687 encoding
= CUPS_WINDOWS_936
;
688 else if (!strcasecmp(charset
, "iso-2022-kr"))
689 encoding
= CUPS_WINDOWS_949
;
690 else if (!strcasecmp(charset
, "big5"))
691 encoding
= CUPS_WINDOWS_950
;
695 DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding
,
696 encoding
== CUPS_AUTO_ENCODING
? "auto" :
697 lang_encodings
[encoding
]));
700 * See if we already have this language/country loaded...
705 snprintf(real
, sizeof(real
), "%s_%s", langname
, country
);
707 #if !defined(__APPLE__) || !defined(CUPS_BUNDLEDIR)
708 snprintf(filename
, sizeof(filename
), "%s/%s/cups_%s.po", cg
->localedir
,
710 #endif /* !__APPLE__ || !CUPS_BUNDLEDIR */
714 strcpy(real
, langname
);
715 #if !defined(__APPLE__) || !defined(CUPS_BUNDLEDIR)
716 filename
[0] = '\0'; /* anti-compiler-warning-code */
717 #endif /* !__APPLE__ || !CUPS_BUNDLEDIR */
720 _cupsMutexLock(&lang_mutex
);
722 if ((lang
= cups_cache_lookup(real
, encoding
)) != NULL
)
724 _cupsMutexUnlock(&lang_mutex
);
726 DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real
));
731 #if !defined(__APPLE__) || !defined(CUPS_BUNDLEDIR)
732 if (!country
[0] || access(filename
, 0))
735 * Country localization not available, look for generic localization...
738 snprintf(filename
, sizeof(filename
), "%s/%s/cups_%s.po", cg
->localedir
,
741 if (access(filename
, 0))
744 * No generic localization, so use POSIX...
747 DEBUG_printf(("4cupsLangGet: access(\"%s\", 0): %s", filename
,
750 snprintf(filename
, sizeof(filename
), "%s/C/cups_C.po", cg
->localedir
);
753 #endif /* !__APPLE__ || !CUPS_BUNDLEDIR */
756 * See if there is a free language available; if so, use that
760 for (lang
= lang_cache
; lang
!= NULL
; lang
= lang
->next
)
767 * Allocate memory for the language and add it to the cache.
770 if ((lang
= calloc(sizeof(cups_lang_t
), 1)) == NULL
)
772 _cupsMutexUnlock(&lang_mutex
);
777 lang
->next
= lang_cache
;
783 * Free all old strings as needed...
786 _cupsMessageFree(lang
->strings
);
790 * Then assign the language and encoding fields...
794 strlcpy(lang
->language
, real
, sizeof(lang
->language
));
796 if (encoding
!= CUPS_AUTO_ENCODING
)
797 lang
->encoding
= encoding
;
799 lang
->encoding
= CUPS_UTF8
;
802 * Read the strings from the file...
805 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
806 lang
->strings
= appleMessageLoad(lang
->language
);
808 lang
->strings
= _cupsMessageLoad(filename
, 1);
809 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
815 _cupsMutexUnlock(&lang_mutex
);
822 * '_cupsLangString()' - Get a message string.
824 * The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to
825 * convert the string to the language encoding.
828 const char * /* O - Localized message */
829 _cupsLangString(cups_lang_t
*lang
, /* I - Language */
830 const char *message
) /* I - Message */
832 const char *s
; /* Localized message */
835 * Range check input...
838 if (!lang
|| !message
)
841 _cupsMutexLock(&lang_mutex
);
843 s
= _cupsMessageLookup(lang
->strings
, message
);
845 _cupsMutexUnlock(&lang_mutex
);
852 * '_cupsMessageFree()' - Free a messages array.
856 _cupsMessageFree(cups_array_t
*a
) /* I - Message array */
858 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
860 * Release the cups.strings dictionary as needed...
863 if (cupsArrayUserData(a
))
864 CFRelease((CFDictionaryRef
)cupsArrayUserData(a
));
865 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
876 * '_cupsMessageLoad()' - Load a .po file into a messages array.
879 cups_array_t
* /* O - New message array */
880 _cupsMessageLoad(const char *filename
, /* I - Message catalog to load */
881 int unquote
) /* I - Unescape \foo in strings? */
883 cups_file_t
*fp
; /* Message file */
884 cups_array_t
*a
; /* Message array */
885 _cups_message_t
*m
; /* Current message */
886 char s
[4096], /* String buffer */
887 *ptr
, /* Pointer into buffer */
888 *temp
; /* New string */
889 int length
; /* Length of combined strings */
892 DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename
));
895 * Create an array to hold the messages...
898 if ((a
= cupsArrayNew3((cups_array_func_t
)cups_message_compare
, NULL
,
899 (cups_ahash_func_t
)NULL
, 0,
900 (cups_acopy_func_t
)NULL
,
901 (cups_afree_func_t
)cups_message_free
)) == NULL
)
903 DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
908 * Open the message catalog file...
911 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
913 DEBUG_printf(("5_cupsMessageLoad: Unable to open file: %s",
919 * Read messages from the catalog file until EOF...
921 * The format is the GNU gettext .po format, which is fairly simple:
924 * msgstr "localized text"
926 * The ID and localized text can span multiple lines using the form:
931 * "localized text spanning "
937 while (cupsFileGets(fp
, s
, sizeof(s
)) != NULL
)
940 * Skip blank and comment lines...
943 if (s
[0] == '#' || !s
[0])
947 * Strip the trailing quote...
950 if ((ptr
= strrchr(s
, '\"')) == NULL
)
956 * Find start of value...
959 if ((ptr
= strchr(s
, '\"')) == NULL
)
965 * Unquote the text...
969 cups_unquote(ptr
, ptr
);
972 * Create or add to a message...
975 if (!strncmp(s
, "msgid", 5))
978 * Add previous message as needed...
985 * Create a new message with the given msgid string...
988 if ((m
= (_cups_message_t
*)calloc(1, sizeof(_cups_message_t
))) == NULL
)
994 if ((m
->id
= strdup(ptr
)) == NULL
)
1001 else if (s
[0] == '\"' && m
)
1004 * Append to current string...
1007 length
= (int)strlen(m
->str
? m
->str
: m
->id
);
1009 if ((temp
= realloc(m
->str
? m
->str
: m
->id
,
1010 length
+ strlen(ptr
) + 1)) == NULL
)
1019 * Copy the new portion to the end of the msgstr string - safe
1020 * to use strcpy because the buffer is allocated to the correct
1026 strcpy(m
->str
+ length
, ptr
);
1031 * Copy the new portion to the end of the msgid string - safe
1032 * to use strcpy because the buffer is allocated to the correct
1038 strcpy(m
->id
+ length
, ptr
);
1041 else if (!strncmp(s
, "msgstr", 6) && m
)
1047 if ((m
->str
= strdup(ptr
)) == NULL
)
1056 * Add the last message string to the array as needed...
1063 * Close the message catalog file and return the new array...
1068 DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
1069 cupsArrayCount(a
)));
1076 * '_cupsMessageLookup()' - Lookup a message string.
1079 const char * /* O - Localized message */
1080 _cupsMessageLookup(cups_array_t
*a
, /* I - Message array */
1081 const char *m
) /* I - Message */
1083 _cups_message_t key
, /* Search key */
1084 *match
; /* Matching message */
1088 * Lookup the message string; if it doesn't exist in the catalog,
1089 * then return the message that was passed to us...
1093 match
= (_cups_message_t
*)cupsArrayFind(a
, &key
);
1095 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1096 if (!match
&& cupsArrayUserData(a
))
1099 * Try looking the string up in the cups.strings dictionary...
1102 CFDictionaryRef dict
; /* cups.strings dictionary */
1103 CFStringRef cfm
, /* Message as a CF string */
1104 cfstr
; /* Localized text as a CF string */
1106 dict
= (CFDictionaryRef
)cupsArrayUserData(a
);
1107 cfm
= CFStringCreateWithCString(kCFAllocatorDefault
, m
,
1108 kCFStringEncodingUTF8
);
1109 match
= calloc(1, sizeof(_cups_message_t
));
1110 match
->id
= strdup(m
);
1111 cfstr
= cfm
? CFDictionaryGetValue(dict
, cfm
) : NULL
;
1115 char buffer
[1024]; /* Message buffer */
1117 CFStringGetCString(cfstr
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
);
1118 match
->str
= strdup(buffer
);
1120 DEBUG_printf(("1_cupsMessageLookup: Found \"%s\" as \"%s\"...",
1125 match
->str
= strdup(m
);
1127 DEBUG_printf(("1_cupsMessageLookup: Did not find \"%s\"...", m
));
1130 cupsArrayAdd(a
, match
);
1135 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
1137 if (match
&& match
->str
)
1138 return (match
->str
);
1146 * 'appleLangDefault()' - Get the default locale string.
1149 static const char * /* O - Locale string */
1150 appleLangDefault(void)
1152 int i
; /* Looping var */
1153 CFBundleRef bundle
; /* Main bundle (if any) */
1154 CFArrayRef bundleList
; /* List of localizations in bundle */
1155 CFPropertyListRef localizationList
;
1156 /* List of localization data */
1157 CFStringRef languageName
; /* Current name */
1158 CFStringRef localeName
; /* Canonical from of name */
1159 char *lang
; /* LANG environment variable */
1160 _cups_globals_t
*cg
= _cupsGlobals();
1161 /* Pointer to library globals */
1164 DEBUG_puts("2appleLangDefault()");
1167 * Only do the lookup and translation the first time.
1170 if (!cg
->language
[0])
1172 if (getenv("SOFTWARE") != NULL
&& (lang
= getenv("LANG")) != NULL
)
1174 strlcpy(cg
->language
, lang
, sizeof(cg
->language
));
1175 return (cg
->language
);
1177 else if ((bundle
= CFBundleGetMainBundle()) != NULL
&&
1178 (bundleList
= CFBundleCopyBundleLocalizations(bundle
)) != NULL
)
1181 CFBundleCopyPreferredLocalizationsFromArray(bundleList
);
1183 CFRelease(bundleList
);
1187 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1188 kCFPreferencesCurrentApplication
);
1190 if (localizationList
)
1192 if (CFGetTypeID(localizationList
) == CFArrayGetTypeID() &&
1193 CFArrayGetCount(localizationList
) > 0)
1195 languageName
= CFArrayGetValueAtIndex(localizationList
, 0);
1198 CFGetTypeID(languageName
) == CFStringGetTypeID())
1200 localeName
= CFLocaleCreateCanonicalLocaleIdentifierFromString(
1201 kCFAllocatorDefault
, languageName
);
1205 CFStringGetCString(localeName
, cg
->language
, sizeof(cg
->language
),
1206 kCFStringEncodingASCII
);
1207 CFRelease(localeName
);
1209 DEBUG_printf(("9appleLangDefault: cg->language=\"%s\"",
1213 * Map new language identifiers to locales...
1217 i
< (int)(sizeof(apple_language_locale
) /
1218 sizeof(apple_language_locale
[0]));
1221 if (!strcmp(cg
->language
, apple_language_locale
[i
].language
))
1223 DEBUG_printf(("9appleLangDefault: mapping \"%s\" to \"%s\"...",
1224 cg
->language
, apple_language_locale
[i
].locale
));
1225 strlcpy(cg
->language
, apple_language_locale
[i
].locale
,
1226 sizeof(cg
->language
));
1232 * Convert language subtag into region subtag...
1235 if (cg
->language
[2] == '-')
1236 cg
->language
[2] = '_';
1238 if (!strchr(cg
->language
, '.'))
1239 strlcat(cg
->language
, ".UTF-8", sizeof(cg
->language
));
1244 CFRelease(localizationList
);
1248 * If we didn't find the language, default to en_US...
1251 if (!cg
->language
[0])
1252 strlcpy(cg
->language
, "en_US.UTF-8", sizeof(cg
->language
));
1256 * Return the cached locale...
1259 return (cg
->language
);
1263 # ifdef CUPS_BUNDLEDIR
1265 * 'appleMessageLoad()' - Load a message catalog from a localizable bundle.
1268 static cups_array_t
* /* O - Message catalog */
1269 appleMessageLoad(const char *locale
) /* I - Locale ID */
1271 char filename
[1024], /* Path to cups.strings file */
1272 applelang
[256]; /* Apple language ID */
1273 CFURLRef url
; /* URL to cups.strings file */
1274 CFReadStreamRef stream
= NULL
; /* File stream */
1275 CFPropertyListRef plist
= NULL
; /* Localization file */
1277 CFErrorRef error
= NULL
; /* Error when opening file */
1281 DEBUG_printf(("appleMessageLoad(locale=\"%s\")", locale
));
1284 * Load the cups.strings file...
1287 snprintf(filename
, sizeof(filename
),
1288 CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings",
1289 _cupsAppleLanguage(locale
, applelang
, sizeof(applelang
)));
1290 DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename
));
1292 if (access(filename
, 0))
1295 * Try alternate lproj directory names...
1298 if (!strncmp(locale
, "en", 2))
1300 else if (!strncmp(locale
, "nb", 2) || !strncmp(locale
, "nl", 2))
1302 else if (!strncmp(locale
, "fr", 2))
1304 else if (!strncmp(locale
, "de", 2))
1306 else if (!strncmp(locale
, "it", 2))
1308 else if (!strncmp(locale
, "ja", 2))
1309 locale
= "Japanese";
1310 else if (!strncmp(locale
, "es", 2))
1313 snprintf(filename
, sizeof(filename
),
1314 CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings", locale
);
1315 DEBUG_printf(("1appleMessageLoad: alternate filename=\"%s\"", filename
));
1318 url
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
,
1320 strlen(filename
), false);
1323 stream
= CFReadStreamCreateWithFile(kCFAllocatorDefault
, url
);
1327 * Read the property list containing the localization data.
1329 * NOTE: This code currently generates a clang "potential leak"
1330 * warning, but the object is released in _cupsMessageFree().
1333 CFReadStreamOpen(stream
);
1336 plist
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, stream
, 0,
1337 kCFPropertyListImmutable
, NULL
,
1341 CFStringRef msg
= CFErrorCopyDescription(error
);
1344 CFStringGetCString(msg
, filename
, sizeof(filename
),
1345 kCFStringEncodingUTF8
);
1346 DEBUG_printf(("1appleMessageLoad: %s", filename
));
1353 plist
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, stream
, 0,
1354 kCFPropertyListImmutable
, NULL
,
1358 if (plist
&& CFGetTypeID(plist
) != CFDictionaryGetTypeID())
1370 DEBUG_printf(("1appleMessageLoad: url=%p, stream=%p, plist=%p", url
, stream
,
1374 * Create and return an empty array to act as a cache for messages, passing the
1375 * plist as the user data.
1378 return (cupsArrayNew3((cups_array_func_t
)cups_message_compare
, (void *)plist
,
1379 (cups_ahash_func_t
)NULL
, 0,
1380 (cups_acopy_func_t
)NULL
,
1381 (cups_afree_func_t
)cups_message_free
));
1383 # endif /* CUPS_BUNDLEDIR */
1384 #endif /* __APPLE__ */
1388 * 'cups_cache_lookup()' - Lookup a language in the cache...
1391 static cups_lang_t
* /* O - Language data or NULL */
1393 const char *name
, /* I - Name of locale */
1394 cups_encoding_t encoding
) /* I - Encoding of locale */
1396 cups_lang_t
*lang
; /* Current language */
1399 DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name
,
1400 encoding
, encoding
== CUPS_AUTO_ENCODING
? "auto" :
1401 lang_encodings
[encoding
]));
1404 * Loop through the cache and return a match if found...
1407 for (lang
= lang_cache
; lang
!= NULL
; lang
= lang
->next
)
1409 DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
1410 "encoding=%d(%s)", lang
, lang
->language
, lang
->encoding
,
1411 lang_encodings
[lang
->encoding
]));
1413 if (!strcmp(lang
->language
, name
) &&
1414 (encoding
== CUPS_AUTO_ENCODING
|| encoding
== lang
->encoding
))
1418 DEBUG_puts("8cups_cache_lookup: returning match!");
1424 DEBUG_puts("8cups_cache_lookup: returning NULL!");
1431 * 'cups_message_compare()' - Compare two messages.
1434 static int /* O - Result of comparison */
1435 cups_message_compare(
1436 _cups_message_t
*m1
, /* I - First message */
1437 _cups_message_t
*m2
) /* I - Second message */
1439 return (strcmp(m1
->id
, m2
->id
));
1444 * 'cups_message_free()' - Free a message.
1448 cups_message_free(_cups_message_t
*m
) /* I - Message */
1461 * 'cups_unquote()' - Unquote characters in strings...
1465 cups_unquote(char *d
, /* O - Unquoted string */
1466 const char *s
) /* I - Original string */
1479 *d
= *d
* 8 + *s
- '0';
1508 * End of "$Id: language.c 7558 2008-05-12 23:46:44Z mike $".