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_message_load() - Load the message catalog for a language.
39 * cups_unquote() - Unquote characters in strings...
43 * Include necessary headers...
46 #include "cups-private.h"
47 #ifdef HAVE_LANGINFO_H
48 # include <langinfo.h>
49 #endif /* HAVE_LANGINFO_H */
55 #ifdef HAVE_COREFOUNDATION_H
56 # include <CoreFoundation/CoreFoundation.h>
57 #endif /* HAVE_COREFOUNDATION_H */
64 static _cups_mutex_t lang_mutex
= _CUPS_MUTEX_INITIALIZER
;
65 /* Mutex to control access to cache */
66 static cups_lang_t
*lang_cache
= NULL
;
67 /* Language string cache */
68 static const char * const lang_encodings
[] =
69 { /* Encoding strings */
70 "us-ascii", "iso-8859-1",
71 "iso-8859-2", "iso-8859-3",
72 "iso-8859-4", "iso-8859-5",
73 "iso-8859-6", "iso-8859-7",
74 "iso-8859-8", "iso-8859-9",
75 "iso-8859-10", "utf-8",
76 "iso-8859-13", "iso-8859-14",
77 "iso-8859-15", "cp874",
83 "koi8-u", "iso-8859-11",
100 "unknown", "unknown",
101 "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",
133 "unknown", "unknown",
142 const char * const language
; /* Language ID */
143 const char * const locale
; /* Locale ID */
144 } _apple_language_locale_t
;
146 static const _apple_language_locale_t apple_language_locale
[] =
147 { /* Locale to language ID LUT */
150 { "zh-Hans", "zh_CN" },
151 { "zh-Hant", "zh_TW" }
153 #endif /* __APPLE__ */
162 static const char *appleLangDefault(void);
163 # ifdef CUPS_BUNDLEDIR
164 # ifndef CF_RETURNS_RETAINED
165 # if __has_feature(attribute_cf_returns_retained)
166 # define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
168 # define CF_RETURNS_RETAINED
169 # endif /* __has_feature(attribute_cf_returns_retained) */
170 # endif /* !CF_RETURNED_RETAINED */
171 static cups_array_t
*appleMessageLoad(const char *locale
)
173 # endif /* CUPS_BUNDLEDIR */
174 #endif /* __APPLE__ */
175 static cups_lang_t
*cups_cache_lookup(const char *name
,
176 cups_encoding_t encoding
);
177 static int cups_message_compare(_cups_message_t
*m1
,
178 _cups_message_t
*m2
);
179 static void cups_message_free(_cups_message_t
*m
);
180 static void cups_message_load(cups_lang_t
*lang
);
181 static void cups_unquote(char *d
, const char *s
);
186 * '_cupsAppleLanguage()' - Get the Apple language identifier associated with a
190 const char * /* O - Language ID */
191 _cupsAppleLanguage(const char *locale
, /* I - Locale ID */
192 char *language
,/* I - Language ID buffer */
193 size_t langsize
) /* I - Size of language ID buffer */
195 int i
; /* Looping var */
196 CFStringRef localeid
, /* CF locale identifier */
197 langid
; /* CF language identifier */
201 * Copy the locale name and convert, as needed, to the Apple-specific
202 * locale identifier...
205 switch (strlen(locale
))
212 strlcpy(language
, "en", langsize
);
216 strlcpy(language
, locale
, langsize
);
220 strlcpy(language
, locale
, langsize
);
222 if (language
[2] == '-')
225 * Convert ll-cc to ll_CC...
229 language
[3] = toupper(language
[3] & 255);
230 language
[4] = toupper(language
[4] & 255);
236 i
< (int)(sizeof(apple_language_locale
) /
237 sizeof(apple_language_locale
[0]));
239 if (!strcmp(locale
, apple_language_locale
[i
].locale
))
241 strlcpy(language
, apple_language_locale
[i
].language
, sizeof(language
));
246 * Attempt to map the locale ID to a language ID...
249 if ((localeid
= CFStringCreateWithCString(kCFAllocatorDefault
, language
,
250 kCFStringEncodingASCII
)) != NULL
)
252 if ((langid
= CFLocaleCreateCanonicalLanguageIdentifierFromString(
253 kCFAllocatorDefault
, localeid
)) != NULL
)
255 CFStringGetCString(langid
, language
, langsize
, kCFStringEncodingASCII
);
263 * Return what we got...
268 #endif /* __APPLE__ */
272 * '_cupsEncodingName()' - Return the character encoding name string
273 * for the given encoding enumeration.
276 const char * /* O - Character encoding */
278 cups_encoding_t encoding
) /* I - Encoding value */
281 encoding
>= (sizeof(lang_encodings
) / sizeof(const char *)))
283 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = out of range (\"%s\")",
284 encoding
, lang_encodings
[0]));
285 return (lang_encodings
[0]);
289 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = \"%s\"",
290 encoding
, lang_encodings
[encoding
]));
291 return (lang_encodings
[encoding
]);
297 * 'cupsLangDefault()' - Return the default language.
300 cups_lang_t
* /* O - Language data */
301 cupsLangDefault(void)
303 return (cupsLangGet(NULL
));
308 * 'cupsLangEncoding()' - Return the character encoding (us-ascii, etc.)
309 * for the given language.
312 const char * /* O - Character encoding */
313 cupsLangEncoding(cups_lang_t
*lang
) /* I - Language data */
316 return ((char*)lang_encodings
[0]);
318 return ((char*)lang_encodings
[lang
->encoding
]);
323 * 'cupsLangFlush()' - Flush all language data out of the cache.
329 cups_lang_t
*lang
, /* Current language */
330 *next
; /* Next language */
334 * Free all languages in the cache...
337 _cupsMutexLock(&lang_mutex
);
339 for (lang
= lang_cache
; lang
!= NULL
; lang
= next
)
342 * Free all messages...
345 _cupsMessageFree(lang
->strings
);
348 * Then free the language structure itself...
357 _cupsMutexUnlock(&lang_mutex
);
362 * 'cupsLangFree()' - Free language data.
364 * This does not actually free anything; use @link cupsLangFlush@ for that.
368 cupsLangFree(cups_lang_t
*lang
) /* I - Language to free */
370 _cupsMutexLock(&lang_mutex
);
372 if (lang
!= NULL
&& lang
->used
> 0)
375 _cupsMutexUnlock(&lang_mutex
);
380 * 'cupsLangGet()' - Get a language.
383 cups_lang_t
* /* O - Language data */
384 cupsLangGet(const char *language
) /* I - Language or locale */
386 int i
; /* Looping var */
388 char locale
[255]; /* Copy of locale name */
389 #endif /* !__APPLE__ */
390 char langname
[16], /* Requested language name */
391 country
[16], /* Country code */
392 charset
[16], /* Character set */
393 *csptr
, /* Pointer to CODESET string */
394 *ptr
, /* Pointer into language/charset */
395 real
[48]; /* Real language name */
396 cups_encoding_t encoding
; /* Encoding to use */
397 cups_lang_t
*lang
; /* Current language... */
398 static const char * const locale_encodings
[] =
399 { /* Locale charset names */
400 "ASCII", "ISO88591", "ISO88592", "ISO88593",
401 "ISO88594", "ISO88595", "ISO88596", "ISO88597",
402 "ISO88598", "ISO88599", "ISO885910", "UTF8",
403 "ISO885913", "ISO885914", "ISO885915", "CP874",
404 "CP1250", "CP1251", "CP1252", "CP1253",
405 "CP1254", "CP1255", "CP1256", "CP1257",
406 "CP1258", "KOI8R", "KOI8U", "ISO885911",
407 "ISO885916", "MACROMAN", "", "",
418 "CP932", "CP936", "CP949", "CP950",
419 "CP1361", "", "", "",
436 "EUCCN", "EUCJP", "EUCKR", "EUCTW",
441 DEBUG_printf(("2cupsLangGet(language=\"%s\")", language
));
445 * Set the character set to UTF-8...
448 strcpy(charset
, "UTF8");
451 * Apple's setlocale doesn't give us the user's localization
452 * preference so we have to look it up this way...
457 if (!getenv("SOFTWARE") || (language
= getenv("LANG")) == NULL
)
458 language
= appleLangDefault();
460 DEBUG_printf(("4cupsLangGet: language=\"%s\"", language
));
465 * Set the charset to "unknown"...
471 * Use setlocale() to determine the currently set locale, and then
472 * fallback to environment variables to avoid setting the locale,
473 * since setlocale() is not thread-safe!
479 * First see if the locale has been set; if it is still "C" or
480 * "POSIX", use the environment to get the default...
484 ptr
= setlocale(LC_MESSAGES
, NULL
);
486 ptr
= setlocale(LC_ALL
, NULL
);
487 # endif /* LC_MESSAGES */
489 DEBUG_printf(("4cupsLangGet: current locale is \"%s\"", ptr
));
491 if (!ptr
|| !strcmp(ptr
, "C") || !strcmp(ptr
, "POSIX"))
494 * Get the character set from the LC_CTYPE locale setting...
497 if ((ptr
= getenv("LC_CTYPE")) == NULL
)
498 if ((ptr
= getenv("LC_ALL")) == NULL
)
499 if ((ptr
= getenv("LANG")) == NULL
)
502 if ((csptr
= strchr(ptr
, '.')) != NULL
)
505 * Extract the character set from the environment...
508 for (ptr
= charset
, csptr
++; *csptr
; csptr
++)
509 if (ptr
< (charset
+ sizeof(charset
) - 1) && _cups_isalnum(*csptr
))
516 * Get the locale for messages from the LC_MESSAGES locale setting...
519 if ((ptr
= getenv("LC_MESSAGES")) == NULL
)
520 if ((ptr
= getenv("LC_ALL")) == NULL
)
521 if ((ptr
= getenv("LANG")) == NULL
)
527 strlcpy(locale
, ptr
, sizeof(locale
));
531 * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
534 if (!strncmp(locale
, "nb", 2))
537 DEBUG_printf(("4cupsLangGet: new language value is \"%s\"", language
));
540 #endif /* __APPLE__ */
543 * If "language" is NULL at this point, then chances are we are using
544 * a language that is not installed for the base OS.
550 * Switch to the POSIX ("C") locale...
558 * On systems that support the nl_langinfo(CODESET) call, use
559 * this value as the character set...
562 if (!charset
[0] && (csptr
= nl_langinfo(CODESET
)) != NULL
)
565 * Copy all of the letters and numbers in the CODESET string...
568 for (ptr
= charset
; *csptr
; csptr
++)
569 if (_cups_isalnum(*csptr
) && ptr
< (charset
+ sizeof(charset
) - 1))
574 DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via "
575 "nl_langinfo(CODESET)...", charset
));
580 * If we don't have a character set by now, default to UTF-8...
584 strcpy(charset
, "UTF8");
587 * Parse the language string passed in to a locale string. "C" is the
588 * standard POSIX locale and is copied unchanged. Otherwise the
589 * language string is converted from ll-cc[.charset] (language-country)
590 * to ll_CC[.CHARSET] to match the file naming convention used by all
591 * POSIX-compliant operating systems. Invalid language names are mapped
592 * to the POSIX locale.
597 if (language
== NULL
|| !language
[0] ||
598 !strcmp(language
, "POSIX"))
599 strcpy(langname
, "C");
603 * Copy the parts of the locale string over safely...
606 for (ptr
= langname
; *language
; language
++)
607 if (*language
== '_' || *language
== '-' || *language
== '.')
609 else if (ptr
< (langname
+ sizeof(langname
) - 1))
610 *ptr
++ = tolower(*language
& 255);
614 if (*language
== '_' || *language
== '-')
617 * Copy the country code...
620 for (language
++, ptr
= country
; *language
; language
++)
621 if (*language
== '.')
623 else if (ptr
< (country
+ sizeof(country
) - 1))
624 *ptr
++ = toupper(*language
& 255);
629 if (*language
== '.' && !charset
[0])
632 * Copy the encoding...
635 for (language
++, ptr
= charset
; *language
; language
++)
636 if (_cups_isalnum(*language
) && ptr
< (charset
+ sizeof(charset
) - 1))
637 *ptr
++ = toupper(*language
& 255);
643 * Force a POSIX locale for an invalid language name...
646 if (strlen(langname
) != 2)
648 strcpy(langname
, "C");
654 DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"",
655 langname
, country
, charset
));
658 * Figure out the desired encoding...
661 encoding
= CUPS_AUTO_ENCODING
;
666 i
< (int)(sizeof(locale_encodings
) / sizeof(locale_encodings
[0]));
668 if (!_cups_strcasecmp(charset
, locale_encodings
[i
]))
670 encoding
= (cups_encoding_t
)i
;
674 if (encoding
== CUPS_AUTO_ENCODING
)
677 * Map alternate names for various character sets...
680 if (!_cups_strcasecmp(charset
, "iso-2022-jp") ||
681 !_cups_strcasecmp(charset
, "sjis"))
682 encoding
= CUPS_WINDOWS_932
;
683 else if (!_cups_strcasecmp(charset
, "iso-2022-cn"))
684 encoding
= CUPS_WINDOWS_936
;
685 else if (!_cups_strcasecmp(charset
, "iso-2022-kr"))
686 encoding
= CUPS_WINDOWS_949
;
687 else if (!_cups_strcasecmp(charset
, "big5"))
688 encoding
= CUPS_WINDOWS_950
;
692 DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding
,
693 encoding
== CUPS_AUTO_ENCODING
? "auto" :
694 lang_encodings
[encoding
]));
697 * See if we already have this language/country loaded...
701 snprintf(real
, sizeof(real
), "%s_%s", langname
, country
);
703 strcpy(real
, langname
);
705 _cupsMutexLock(&lang_mutex
);
707 if ((lang
= cups_cache_lookup(real
, encoding
)) != NULL
)
709 _cupsMutexUnlock(&lang_mutex
);
711 DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real
));
717 * See if there is a free language available; if so, use that
721 for (lang
= lang_cache
; lang
!= NULL
; lang
= lang
->next
)
728 * Allocate memory for the language and add it to the cache.
731 if ((lang
= calloc(sizeof(cups_lang_t
), 1)) == NULL
)
733 _cupsMutexUnlock(&lang_mutex
);
738 lang
->next
= lang_cache
;
744 * Free all old strings as needed...
747 _cupsMessageFree(lang
->strings
);
748 lang
->strings
= NULL
;
752 * Then assign the language and encoding fields...
756 strlcpy(lang
->language
, real
, sizeof(lang
->language
));
758 if (encoding
!= CUPS_AUTO_ENCODING
)
759 lang
->encoding
= encoding
;
761 lang
->encoding
= CUPS_UTF8
;
767 _cupsMutexUnlock(&lang_mutex
);
774 * '_cupsLangString()' - Get a message string.
776 * The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to
777 * convert the string to the language encoding.
780 const char * /* O - Localized message */
781 _cupsLangString(cups_lang_t
*lang
, /* I - Language */
782 const char *message
) /* I - Message */
784 const char *s
; /* Localized message */
787 * Range check input...
790 if (!lang
|| !message
)
793 _cupsMutexLock(&lang_mutex
);
796 * Load the message catalog if needed...
800 cups_message_load(lang
);
802 s
= _cupsMessageLookup(lang
->strings
, message
);
804 _cupsMutexUnlock(&lang_mutex
);
811 * '_cupsMessageFree()' - Free a messages array.
815 _cupsMessageFree(cups_array_t
*a
) /* I - Message array */
817 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
819 * Release the cups.strings dictionary as needed...
822 if (cupsArrayUserData(a
))
823 CFRelease((CFDictionaryRef
)cupsArrayUserData(a
));
824 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
835 * '_cupsMessageLoad()' - Load a .po file into a messages array.
838 cups_array_t
* /* O - New message array */
839 _cupsMessageLoad(const char *filename
, /* I - Message catalog to load */
840 int unquote
) /* I - Unescape \foo in strings? */
842 cups_file_t
*fp
; /* Message file */
843 cups_array_t
*a
; /* Message array */
844 _cups_message_t
*m
; /* Current message */
845 char s
[4096], /* String buffer */
846 *ptr
, /* Pointer into buffer */
847 *temp
; /* New string */
848 int length
; /* Length of combined strings */
851 DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename
));
854 * Create an array to hold the messages...
857 if ((a
= cupsArrayNew3((cups_array_func_t
)cups_message_compare
, NULL
,
858 (cups_ahash_func_t
)NULL
, 0,
859 (cups_acopy_func_t
)NULL
,
860 (cups_afree_func_t
)cups_message_free
)) == NULL
)
862 DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
867 * Open the message catalog file...
870 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
872 DEBUG_printf(("5_cupsMessageLoad: Unable to open file: %s",
878 * Read messages from the catalog file until EOF...
880 * The format is the GNU gettext .po format, which is fairly simple:
883 * msgstr "localized text"
885 * The ID and localized text can span multiple lines using the form:
890 * "localized text spanning "
896 while (cupsFileGets(fp
, s
, sizeof(s
)) != NULL
)
899 * Skip blank and comment lines...
902 if (s
[0] == '#' || !s
[0])
906 * Strip the trailing quote...
909 if ((ptr
= strrchr(s
, '\"')) == NULL
)
915 * Find start of value...
918 if ((ptr
= strchr(s
, '\"')) == NULL
)
924 * Unquote the text...
928 cups_unquote(ptr
, ptr
);
931 * Create or add to a message...
934 if (!strncmp(s
, "msgid", 5))
937 * Add previous message as needed...
944 * Create a new message with the given msgid string...
947 if ((m
= (_cups_message_t
*)calloc(1, sizeof(_cups_message_t
))) == NULL
)
953 if ((m
->id
= strdup(ptr
)) == NULL
)
960 else if (s
[0] == '\"' && m
)
963 * Append to current string...
966 length
= (int)strlen(m
->str
? m
->str
: m
->id
);
968 if ((temp
= realloc(m
->str
? m
->str
: m
->id
,
969 length
+ strlen(ptr
) + 1)) == NULL
)
978 * Copy the new portion to the end of the msgstr string - safe
979 * to use strcpy because the buffer is allocated to the correct
985 strcpy(m
->str
+ length
, ptr
);
990 * Copy the new portion to the end of the msgid string - safe
991 * to use strcpy because the buffer is allocated to the correct
997 strcpy(m
->id
+ length
, ptr
);
1000 else if (!strncmp(s
, "msgstr", 6) && m
)
1006 if ((m
->str
= strdup(ptr
)) == NULL
)
1015 * Add the last message string to the array as needed...
1022 * Close the message catalog file and return the new array...
1027 DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
1028 cupsArrayCount(a
)));
1035 * '_cupsMessageLookup()' - Lookup a message string.
1038 const char * /* O - Localized message */
1039 _cupsMessageLookup(cups_array_t
*a
, /* I - Message array */
1040 const char *m
) /* I - Message */
1042 _cups_message_t key
, /* Search key */
1043 *match
; /* Matching message */
1047 * Lookup the message string; if it doesn't exist in the catalog,
1048 * then return the message that was passed to us...
1052 match
= (_cups_message_t
*)cupsArrayFind(a
, &key
);
1054 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1055 if (!match
&& cupsArrayUserData(a
))
1058 * Try looking the string up in the cups.strings dictionary...
1061 CFDictionaryRef dict
; /* cups.strings dictionary */
1062 CFStringRef cfm
, /* Message as a CF string */
1063 cfstr
; /* Localized text as a CF string */
1065 dict
= (CFDictionaryRef
)cupsArrayUserData(a
);
1066 cfm
= CFStringCreateWithCString(kCFAllocatorDefault
, m
,
1067 kCFStringEncodingUTF8
);
1068 match
= calloc(1, sizeof(_cups_message_t
));
1069 match
->id
= strdup(m
);
1070 cfstr
= cfm
? CFDictionaryGetValue(dict
, cfm
) : NULL
;
1074 char buffer
[1024]; /* Message buffer */
1076 CFStringGetCString(cfstr
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
);
1077 match
->str
= strdup(buffer
);
1079 DEBUG_printf(("1_cupsMessageLookup: Found \"%s\" as \"%s\"...",
1084 match
->str
= strdup(m
);
1086 DEBUG_printf(("1_cupsMessageLookup: Did not find \"%s\"...", m
));
1089 cupsArrayAdd(a
, match
);
1094 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
1096 if (match
&& match
->str
)
1097 return (match
->str
);
1105 * 'appleLangDefault()' - Get the default locale string.
1108 static const char * /* O - Locale string */
1109 appleLangDefault(void)
1111 int i
; /* Looping var */
1112 CFBundleRef bundle
; /* Main bundle (if any) */
1113 CFArrayRef bundleList
; /* List of localizations in bundle */
1114 CFPropertyListRef localizationList
;
1115 /* List of localization data */
1116 CFStringRef languageName
; /* Current name */
1117 CFStringRef localeName
; /* Canonical from of name */
1118 char *lang
; /* LANG environment variable */
1119 _cups_globals_t
*cg
= _cupsGlobals();
1120 /* Pointer to library globals */
1123 DEBUG_puts("2appleLangDefault()");
1126 * Only do the lookup and translation the first time.
1129 if (!cg
->language
[0])
1131 if (getenv("SOFTWARE") != NULL
&& (lang
= getenv("LANG")) != NULL
)
1133 strlcpy(cg
->language
, lang
, sizeof(cg
->language
));
1134 return (cg
->language
);
1136 else if ((bundle
= CFBundleGetMainBundle()) != NULL
&&
1137 (bundleList
= CFBundleCopyBundleLocalizations(bundle
)) != NULL
)
1140 CFBundleCopyPreferredLocalizationsFromArray(bundleList
);
1142 CFRelease(bundleList
);
1146 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1147 kCFPreferencesCurrentApplication
);
1149 if (localizationList
)
1151 if (CFGetTypeID(localizationList
) == CFArrayGetTypeID() &&
1152 CFArrayGetCount(localizationList
) > 0)
1154 languageName
= CFArrayGetValueAtIndex(localizationList
, 0);
1157 CFGetTypeID(languageName
) == CFStringGetTypeID())
1159 localeName
= CFLocaleCreateCanonicalLocaleIdentifierFromString(
1160 kCFAllocatorDefault
, languageName
);
1164 CFStringGetCString(localeName
, cg
->language
, sizeof(cg
->language
),
1165 kCFStringEncodingASCII
);
1166 CFRelease(localeName
);
1168 DEBUG_printf(("9appleLangDefault: cg->language=\"%s\"",
1172 * Map new language identifiers to locales...
1176 i
< (int)(sizeof(apple_language_locale
) /
1177 sizeof(apple_language_locale
[0]));
1180 if (!strcmp(cg
->language
, apple_language_locale
[i
].language
))
1182 DEBUG_printf(("9appleLangDefault: mapping \"%s\" to \"%s\"...",
1183 cg
->language
, apple_language_locale
[i
].locale
));
1184 strlcpy(cg
->language
, apple_language_locale
[i
].locale
,
1185 sizeof(cg
->language
));
1191 * Convert language subtag into region subtag...
1194 if (cg
->language
[2] == '-')
1195 cg
->language
[2] = '_';
1197 if (!strchr(cg
->language
, '.'))
1198 strlcat(cg
->language
, ".UTF-8", sizeof(cg
->language
));
1203 CFRelease(localizationList
);
1207 * If we didn't find the language, default to en_US...
1210 if (!cg
->language
[0])
1211 strlcpy(cg
->language
, "en_US.UTF-8", sizeof(cg
->language
));
1215 * Return the cached locale...
1218 return (cg
->language
);
1222 # ifdef CUPS_BUNDLEDIR
1224 * 'appleMessageLoad()' - Load a message catalog from a localizable bundle.
1227 static cups_array_t
* /* O - Message catalog */
1228 appleMessageLoad(const char *locale
) /* I - Locale ID */
1230 char filename
[1024], /* Path to cups.strings file */
1231 applelang
[256]; /* Apple language ID */
1232 CFURLRef url
; /* URL to cups.strings file */
1233 CFReadStreamRef stream
= NULL
; /* File stream */
1234 CFPropertyListRef plist
= NULL
; /* Localization file */
1236 CFErrorRef error
= NULL
; /* Error when opening file */
1240 DEBUG_printf(("appleMessageLoad(locale=\"%s\")", locale
));
1243 * Load the cups.strings file...
1246 snprintf(filename
, sizeof(filename
),
1247 CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings",
1248 _cupsAppleLanguage(locale
, applelang
, sizeof(applelang
)));
1249 DEBUG_printf(("1appleMessageLoad: filename=\"%s\"", filename
));
1251 if (access(filename
, 0))
1254 * Try alternate lproj directory names...
1257 if (!strncmp(locale
, "en", 2))
1259 else if (!strncmp(locale
, "nb", 2) || !strncmp(locale
, "nl", 2))
1261 else if (!strncmp(locale
, "fr", 2))
1263 else if (!strncmp(locale
, "de", 2))
1265 else if (!strncmp(locale
, "it", 2))
1267 else if (!strncmp(locale
, "ja", 2))
1268 locale
= "Japanese";
1269 else if (!strncmp(locale
, "es", 2))
1272 snprintf(filename
, sizeof(filename
),
1273 CUPS_BUNDLEDIR
"/Resources/%s.lproj/cups.strings", locale
);
1274 DEBUG_printf(("1appleMessageLoad: alternate filename=\"%s\"", filename
));
1277 url
= CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault
,
1279 strlen(filename
), false);
1282 stream
= CFReadStreamCreateWithFile(kCFAllocatorDefault
, url
);
1286 * Read the property list containing the localization data.
1288 * NOTE: This code currently generates a clang "potential leak"
1289 * warning, but the object is released in _cupsMessageFree().
1292 CFReadStreamOpen(stream
);
1295 plist
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, stream
, 0,
1296 kCFPropertyListImmutable
, NULL
,
1300 CFStringRef msg
= CFErrorCopyDescription(error
);
1303 CFStringGetCString(msg
, filename
, sizeof(filename
),
1304 kCFStringEncodingUTF8
);
1305 DEBUG_printf(("1appleMessageLoad: %s", filename
));
1312 plist
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, stream
, 0,
1313 kCFPropertyListImmutable
, NULL
,
1317 if (plist
&& CFGetTypeID(plist
) != CFDictionaryGetTypeID())
1329 DEBUG_printf(("1appleMessageLoad: url=%p, stream=%p, plist=%p", url
, stream
,
1333 * Create and return an empty array to act as a cache for messages, passing the
1334 * plist as the user data.
1337 return (cupsArrayNew3((cups_array_func_t
)cups_message_compare
, (void *)plist
,
1338 (cups_ahash_func_t
)NULL
, 0,
1339 (cups_acopy_func_t
)NULL
,
1340 (cups_afree_func_t
)cups_message_free
));
1342 # endif /* CUPS_BUNDLEDIR */
1343 #endif /* __APPLE__ */
1347 * 'cups_cache_lookup()' - Lookup a language in the cache...
1350 static cups_lang_t
* /* O - Language data or NULL */
1352 const char *name
, /* I - Name of locale */
1353 cups_encoding_t encoding
) /* I - Encoding of locale */
1355 cups_lang_t
*lang
; /* Current language */
1358 DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name
,
1359 encoding
, encoding
== CUPS_AUTO_ENCODING
? "auto" :
1360 lang_encodings
[encoding
]));
1363 * Loop through the cache and return a match if found...
1366 for (lang
= lang_cache
; lang
!= NULL
; lang
= lang
->next
)
1368 DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
1369 "encoding=%d(%s)", lang
, lang
->language
, lang
->encoding
,
1370 lang_encodings
[lang
->encoding
]));
1372 if (!strcmp(lang
->language
, name
) &&
1373 (encoding
== CUPS_AUTO_ENCODING
|| encoding
== lang
->encoding
))
1377 DEBUG_puts("8cups_cache_lookup: returning match!");
1383 DEBUG_puts("8cups_cache_lookup: returning NULL!");
1390 * 'cups_message_compare()' - Compare two messages.
1393 static int /* O - Result of comparison */
1394 cups_message_compare(
1395 _cups_message_t
*m1
, /* I - First message */
1396 _cups_message_t
*m2
) /* I - Second message */
1398 return (strcmp(m1
->id
, m2
->id
));
1403 * 'cups_message_free()' - Free a message.
1407 cups_message_free(_cups_message_t
*m
) /* I - Message */
1420 * 'cups_message_load()' - Load the message catalog for a language.
1424 cups_message_load(cups_lang_t
*lang
) /* I - Language */
1426 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1427 lang
->strings
= appleMessageLoad(lang
->language
);
1430 char filename
[1024]; /* Filename for language locale file */
1431 _cups_globals_t
*cg
= _cupsGlobals();
1432 /* Pointer to library globals */
1435 snprintf(filename
, sizeof(filename
), "%s/%s/cups_%s.po", cg
->localedir
,
1436 lang
->language
, lang
->language
);
1438 if (strchr(lang
->language
, '_') && access(filename
, 0))
1441 * Country localization not available, look for generic localization...
1444 snprintf(filename
, sizeof(filename
), "%s/%.2s/cups_%.2s.po", cg
->localedir
,
1445 lang
->language
, lang
->language
);
1447 if (access(filename
, 0))
1450 * No generic localization, so use POSIX...
1453 DEBUG_printf(("4cups_message_load: access(\"%s\", 0): %s", filename
,
1456 snprintf(filename
, sizeof(filename
), "%s/C/cups_C.po", cg
->localedir
);
1461 * Read the strings from the file...
1464 lang
->strings
= _cupsMessageLoad(filename
, 1);
1465 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
1470 * 'cups_unquote()' - Unquote characters in strings...
1474 cups_unquote(char *d
, /* O - Unquoted string */
1475 const char *s
) /* I - Original string */
1488 *d
= *d
* 8 + *s
- '0';
1517 * End of "$Id: language.c 7558 2008-05-12 23:46:44Z mike $".