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__ */
160 static const char *appleLangDefault(void);
161 # ifdef CUPS_BUNDLEDIR
162 static cups_array_t
*appleMessageLoad(const char *locale
);
163 # endif /* CUPS_BUNDLEDIR */
164 #endif /* __APPLE__ */
165 static cups_lang_t
*cups_cache_lookup(const char *name
,
166 cups_encoding_t encoding
);
167 static int cups_message_compare(_cups_message_t
*m1
,
168 _cups_message_t
*m2
);
169 static void cups_message_free(_cups_message_t
*m
);
170 static void cups_unquote(char *d
, const char *s
);
175 * '_cupsAppleLanguage()' - Get the Apple language identifier associated with a
179 const char * /* O - Language ID */
180 _cupsAppleLanguage(const char *locale
, /* I - Locale ID */
181 char *language
,/* I - Language ID buffer */
182 size_t langsize
) /* I - Size of language ID buffer */
184 int i
; /* Looping var */
185 CFStringRef localeid
, /* CF locale identifier */
186 langid
; /* CF language identifier */
190 * Copy the locale name and convert, as needed, to the Apple-specific
191 * locale identifier...
194 switch (strlen(locale
))
201 strlcpy(language
, "en", langsize
);
205 strlcpy(language
, locale
, langsize
);
209 strlcpy(language
, locale
, langsize
);
211 if (language
[2] == '-')
214 * Convert ll-cc to ll_CC...
218 language
[3] = toupper(language
[3] & 255);
219 language
[4] = toupper(language
[4] & 255);
225 i
< (int)(sizeof(apple_language_locale
) /
226 sizeof(apple_language_locale
[0]));
228 if (!strcmp(locale
, apple_language_locale
[i
].locale
))
230 strlcpy(language
, apple_language_locale
[i
].language
, sizeof(language
));
235 * Attempt to map the locale ID to a language ID...
238 if ((localeid
= CFStringCreateWithCString(kCFAllocatorDefault
, language
,
239 kCFStringEncodingASCII
)) != NULL
)
241 if ((langid
= CFLocaleCreateCanonicalLanguageIdentifierFromString(
242 kCFAllocatorDefault
, localeid
)) != NULL
)
244 CFStringGetCString(langid
, language
, langsize
, kCFStringEncodingASCII
);
252 * Return what we got...
257 #endif /* __APPLE__ */
261 * '_cupsEncodingName()' - Return the character encoding name string
262 * for the given encoding enumeration.
265 const char * /* O - Character encoding */
267 cups_encoding_t encoding
) /* I - Encoding value */
270 encoding
>= (sizeof(lang_encodings
) / sizeof(const char *)))
272 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = out of range (\"%s\")",
273 encoding
, lang_encodings
[0]));
274 return (lang_encodings
[0]);
278 DEBUG_printf(("1_cupsEncodingName(encoding=%d) = \"%s\"",
279 encoding
, lang_encodings
[encoding
]));
280 return (lang_encodings
[encoding
]);
286 * 'cupsLangDefault()' - Return the default language.
289 cups_lang_t
* /* O - Language data */
290 cupsLangDefault(void)
292 return (cupsLangGet(NULL
));
297 * 'cupsLangEncoding()' - Return the character encoding (us-ascii, etc.)
298 * for the given language.
301 const char * /* O - Character encoding */
302 cupsLangEncoding(cups_lang_t
*lang
) /* I - Language data */
305 return ((char*)lang_encodings
[0]);
307 return ((char*)lang_encodings
[lang
->encoding
]);
312 * 'cupsLangFlush()' - Flush all language data out of the cache.
318 cups_lang_t
*lang
, /* Current language */
319 *next
; /* Next language */
323 * Free all languages in the cache...
326 _cupsMutexLock(&lang_mutex
);
328 for (lang
= lang_cache
; lang
!= NULL
; lang
= next
)
331 * Free all messages...
334 _cupsMessageFree(lang
->strings
);
337 * Then free the language structure itself...
346 _cupsMutexUnlock(&lang_mutex
);
351 * 'cupsLangFree()' - Free language data.
353 * This does not actually free anything; use @link cupsLangFlush@ for that.
357 cupsLangFree(cups_lang_t
*lang
) /* I - Language to free */
359 _cupsMutexLock(&lang_mutex
);
361 if (lang
!= NULL
&& lang
->used
> 0)
364 _cupsMutexUnlock(&lang_mutex
);
369 * 'cupsLangGet()' - Get a language.
372 cups_lang_t
* /* O - Language data */
373 cupsLangGet(const char *language
) /* I - Language or locale */
375 int i
; /* Looping var */
377 char locale
[255]; /* Copy of locale name */
378 #endif /* !__APPLE__ */
379 char langname
[16], /* Requested language name */
380 country
[16], /* Country code */
381 charset
[16], /* Character set */
382 *csptr
, /* Pointer to CODESET string */
383 *ptr
, /* Pointer into language/charset */
384 real
[48]; /* Real language name */
385 cups_encoding_t encoding
; /* Encoding to use */
386 cups_lang_t
*lang
; /* Current language... */
387 #if !defined(__APPLE__) || !defined(CUPS_BUNDLEDIR)
388 char filename
[1024]; /* Filename for language locale file */
389 _cups_globals_t
*cg
= _cupsGlobals();
390 /* Pointer to library globals */
391 #endif /* !__APPLE__ || !CUPS_BUNDLEDIR */
392 static const char * const locale_encodings
[] =
393 { /* Locale charset names */
394 "ASCII", "ISO88591", "ISO88592", "ISO88593",
395 "ISO88594", "ISO88595", "ISO88596", "ISO88597",
396 "ISO88598", "ISO88599", "ISO885910", "UTF8",
397 "ISO885913", "ISO885914", "ISO885915", "CP874",
398 "CP1250", "CP1251", "CP1252", "CP1253",
399 "CP1254", "CP1255", "CP1256", "CP1257",
400 "CP1258", "KOI8R", "KOI8U", "ISO885911",
401 "ISO885916", "MACROMAN", "", "",
412 "CP932", "CP936", "CP949", "CP950",
413 "CP1361", "", "", "",
430 "EUCCN", "EUCJP", "EUCKR", "EUCTW",
435 DEBUG_printf(("2cupsLangGet(language=\"%s\")", language
));
439 * Set the character set to UTF-8...
442 strcpy(charset
, "UTF8");
445 * Apple's setlocale doesn't give us the user's localization
446 * preference so we have to look it up this way...
451 if (!getenv("SOFTWARE") || (language
= getenv("LANG")) == NULL
)
452 language
= appleLangDefault();
454 DEBUG_printf(("4cupsLangGet: language=\"%s\"", language
));
459 * Set the charset to "unknown"...
465 * Use setlocale() to determine the currently set locale, and then
466 * fallback to environment variables to avoid setting the locale,
467 * since setlocale() is not thread-safe!
473 * First see if the locale has been set; if it is still "C" or
474 * "POSIX", use the environment to get the default...
478 ptr
= setlocale(LC_MESSAGES
, NULL
);
480 ptr
= setlocale(LC_ALL
, NULL
);
481 # endif /* LC_MESSAGES */
483 DEBUG_printf(("4cupsLangGet: current locale is \"%s\"", ptr
));
485 if (!ptr
|| !strcmp(ptr
, "C") || !strcmp(ptr
, "POSIX"))
488 * Get the character set from the LC_CTYPE locale setting...
491 if ((ptr
= getenv("LC_CTYPE")) == NULL
)
492 if ((ptr
= getenv("LC_ALL")) == NULL
)
493 if ((ptr
= getenv("LANG")) == NULL
)
496 if ((csptr
= strchr(ptr
, '.')) != NULL
)
499 * Extract the character set from the environment...
502 for (ptr
= charset
, csptr
++; *csptr
; csptr
++)
503 if (ptr
< (charset
+ sizeof(charset
) - 1) && _cups_isalnum(*csptr
))
510 * Get the locale for messages from the LC_MESSAGES locale setting...
513 if ((ptr
= getenv("LC_MESSAGES")) == NULL
)
514 if ((ptr
= getenv("LC_ALL")) == NULL
)
515 if ((ptr
= getenv("LANG")) == NULL
)
521 strlcpy(locale
, ptr
, sizeof(locale
));
525 * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
528 if (!strncmp(locale
, "nb", 2))
531 DEBUG_printf(("4cupsLangGet: new language value is \"%s\"", language
));
534 #endif /* __APPLE__ */
537 * If "language" is NULL at this point, then chances are we are using
538 * a language that is not installed for the base OS.
544 * Switch to the POSIX ("C") locale...
552 * On systems that support the nl_langinfo(CODESET) call, use
553 * this value as the character set...
556 if (!charset
[0] && (csptr
= nl_langinfo(CODESET
)) != NULL
)
559 * Copy all of the letters and numbers in the CODESET string...
562 for (ptr
= charset
; *csptr
; csptr
++)
563 if (_cups_isalnum(*csptr
) && ptr
< (charset
+ sizeof(charset
) - 1))
568 DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via "
569 "nl_langinfo(CODESET)...", charset
));
574 * If we don't have a character set by now, default to UTF-8...
578 strcpy(charset
, "UTF8");
581 * Parse the language string passed in to a locale string. "C" is the
582 * standard POSIX locale and is copied unchanged. Otherwise the
583 * language string is converted from ll-cc[.charset] (language-country)
584 * to ll_CC[.CHARSET] to match the file naming convention used by all
585 * POSIX-compliant operating systems. Invalid language names are mapped
586 * to the POSIX locale.
591 if (language
== NULL
|| !language
[0] ||
592 !strcmp(language
, "POSIX"))
593 strcpy(langname
, "C");
597 * Copy the parts of the locale string over safely...
600 for (ptr
= langname
; *language
; language
++)
601 if (*language
== '_' || *language
== '-' || *language
== '.')
603 else if (ptr
< (langname
+ sizeof(langname
) - 1))
604 *ptr
++ = tolower(*language
& 255);
608 if (*language
== '_' || *language
== '-')
611 * Copy the country code...
614 for (language
++, ptr
= country
; *language
; language
++)
615 if (*language
== '.')
617 else if (ptr
< (country
+ sizeof(country
) - 1))
618 *ptr
++ = toupper(*language
& 255);
623 if (*language
== '.' && !charset
[0])
626 * Copy the encoding...
629 for (language
++, ptr
= charset
; *language
; language
++)
630 if (_cups_isalnum(*language
) && ptr
< (charset
+ sizeof(charset
) - 1))
631 *ptr
++ = toupper(*language
& 255);
637 * Force a POSIX locale for an invalid language name...
640 if (strlen(langname
) != 2)
642 strcpy(langname
, "C");
648 DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"",
649 langname
, country
, charset
));
652 * Figure out the desired encoding...
655 encoding
= CUPS_AUTO_ENCODING
;
660 i
< (int)(sizeof(locale_encodings
) / sizeof(locale_encodings
[0]));
662 if (!strcasecmp(charset
, locale_encodings
[i
]))
664 encoding
= (cups_encoding_t
)i
;
668 if (encoding
== CUPS_AUTO_ENCODING
)
671 * Map alternate names for various character sets...
674 if (!strcasecmp(charset
, "iso-2022-jp") ||
675 !strcasecmp(charset
, "sjis"))
676 encoding
= CUPS_WINDOWS_932
;
677 else if (!strcasecmp(charset
, "iso-2022-cn"))
678 encoding
= CUPS_WINDOWS_936
;
679 else if (!strcasecmp(charset
, "iso-2022-kr"))
680 encoding
= CUPS_WINDOWS_949
;
681 else if (!strcasecmp(charset
, "big5"))
682 encoding
= CUPS_WINDOWS_950
;
686 DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding
,
687 encoding
== CUPS_AUTO_ENCODING
? "auto" :
688 lang_encodings
[encoding
]));
691 * See if we already have this language/country loaded...
696 snprintf(real
, sizeof(real
), "%s_%s", langname
, country
);
698 #if !defined(__APPLE__) || !defined(CUPS_BUNDLEDIR)
699 snprintf(filename
, sizeof(filename
), "%s/%s/cups_%s.po", cg
->localedir
,
701 #endif /* !__APPLE__ || !CUPS_BUNDLEDIR */
705 strcpy(real
, langname
);
706 #if !defined(__APPLE__) || !defined(CUPS_BUNDLEDIR)
707 filename
[0] = '\0'; /* anti-compiler-warning-code */
708 #endif /* !__APPLE__ || !CUPS_BUNDLEDIR */
711 _cupsMutexLock(&lang_mutex
);
713 if ((lang
= cups_cache_lookup(real
, encoding
)) != NULL
)
715 _cupsMutexUnlock(&lang_mutex
);
717 DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real
));
722 #if !defined(__APPLE__) || !defined(CUPS_BUNDLEDIR)
723 if (!country
[0] || access(filename
, 0))
726 * Country localization not available, look for generic localization...
729 snprintf(filename
, sizeof(filename
), "%s/%s/cups_%s.po", cg
->localedir
,
732 if (access(filename
, 0))
735 * No generic localization, so use POSIX...
738 DEBUG_printf(("4cupsLangGet: access(\"%s\", 0): %s", filename
,
741 snprintf(filename
, sizeof(filename
), "%s/C/cups_C.po", cg
->localedir
);
744 #endif /* !__APPLE__ || !CUPS_BUNDLEDIR */
747 * See if there is a free language available; if so, use that
751 for (lang
= lang_cache
; lang
!= NULL
; lang
= lang
->next
)
758 * Allocate memory for the language and add it to the cache.
761 if ((lang
= calloc(sizeof(cups_lang_t
), 1)) == NULL
)
763 _cupsMutexUnlock(&lang_mutex
);
768 lang
->next
= lang_cache
;
774 * Free all old strings as needed...
777 _cupsMessageFree(lang
->strings
);
781 * Then assign the language and encoding fields...
785 strlcpy(lang
->language
, real
, sizeof(lang
->language
));
787 if (encoding
!= CUPS_AUTO_ENCODING
)
788 lang
->encoding
= encoding
;
790 lang
->encoding
= CUPS_UTF8
;
793 * Read the strings from the file...
796 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
797 lang
->strings
= appleMessageLoad(lang
->language
);
799 lang
->strings
= _cupsMessageLoad(filename
, 1);
800 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
806 _cupsMutexUnlock(&lang_mutex
);
813 * '_cupsLangString()' - Get a message string.
815 * The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to
816 * convert the string to the language encoding.
819 const char * /* O - Localized message */
820 _cupsLangString(cups_lang_t
*lang
, /* I - Language */
821 const char *message
) /* I - Message */
823 const char *s
; /* Localized message */
826 * Range check input...
829 if (!lang
|| !message
)
832 _cupsMutexLock(&lang_mutex
);
834 s
= _cupsMessageLookup(lang
->strings
, message
);
836 _cupsMutexUnlock(&lang_mutex
);
843 * '_cupsMessageFree()' - Free a messages array.
847 _cupsMessageFree(cups_array_t
*a
) /* I - Message array */
849 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
851 * Release the cups.strings dictionary as needed...
854 if (cupsArrayUserData(a
))
855 CFRelease((CFDictionaryRef
)cupsArrayUserData(a
));
856 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
867 * '_cupsMessageLoad()' - Load a .po file into a messages array.
870 cups_array_t
* /* O - New message array */
871 _cupsMessageLoad(const char *filename
, /* I - Message catalog to load */
872 int unquote
) /* I - Unescape \foo in strings? */
874 cups_file_t
*fp
; /* Message file */
875 cups_array_t
*a
; /* Message array */
876 _cups_message_t
*m
; /* Current message */
877 char s
[4096], /* String buffer */
878 *ptr
, /* Pointer into buffer */
879 *temp
; /* New string */
880 int length
; /* Length of combined strings */
883 DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename
));
886 * Create an array to hold the messages...
889 if ((a
= cupsArrayNew3((cups_array_func_t
)cups_message_compare
, NULL
,
890 (cups_ahash_func_t
)NULL
, 0,
891 (cups_acopy_func_t
)NULL
,
892 (cups_afree_func_t
)cups_message_free
)) == NULL
)
894 DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
899 * Open the message catalog file...
902 if ((fp
= cupsFileOpen(filename
, "r")) == NULL
)
904 DEBUG_printf(("5_cupsMessageLoad: Unable to open file: %s",
910 * Read messages from the catalog file until EOF...
912 * The format is the GNU gettext .po format, which is fairly simple:
915 * msgstr "localized text"
917 * The ID and localized text can span multiple lines using the form:
922 * "localized text spanning "
928 while (cupsFileGets(fp
, s
, sizeof(s
)) != NULL
)
931 * Skip blank and comment lines...
934 if (s
[0] == '#' || !s
[0])
938 * Strip the trailing quote...
941 if ((ptr
= strrchr(s
, '\"')) == NULL
)
947 * Find start of value...
950 if ((ptr
= strchr(s
, '\"')) == NULL
)
956 * Unquote the text...
960 cups_unquote(ptr
, ptr
);
963 * Create or add to a message...
966 if (!strncmp(s
, "msgid", 5))
969 * Add previous message as needed...
976 * Create a new message with the given msgid string...
979 if ((m
= (_cups_message_t
*)calloc(1, sizeof(_cups_message_t
))) == NULL
)
985 if ((m
->id
= strdup(ptr
)) == NULL
)
992 else if (s
[0] == '\"' && m
)
995 * Append to current string...
998 length
= (int)strlen(m
->str
? m
->str
: m
->id
);
1000 if ((temp
= realloc(m
->str
? m
->str
: m
->id
,
1001 length
+ strlen(ptr
) + 1)) == NULL
)
1010 * Copy the new portion to the end of the msgstr string - safe
1011 * to use strcpy because the buffer is allocated to the correct
1017 strcpy(m
->str
+ length
, ptr
);
1022 * Copy the new portion to the end of the msgid string - safe
1023 * to use strcpy because the buffer is allocated to the correct
1029 strcpy(m
->id
+ length
, ptr
);
1032 else if (!strncmp(s
, "msgstr", 6) && m
)
1038 if ((m
->str
= strdup(ptr
)) == NULL
)
1047 * Add the last message string to the array as needed...
1054 * Close the message catalog file and return the new array...
1059 DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
1060 cupsArrayCount(a
)));
1067 * '_cupsMessageLookup()' - Lookup a message string.
1070 const char * /* O - Localized message */
1071 _cupsMessageLookup(cups_array_t
*a
, /* I - Message array */
1072 const char *m
) /* I - Message */
1074 _cups_message_t key
, /* Search key */
1075 *match
; /* Matching message */
1079 * Lookup the message string; if it doesn't exist in the catalog,
1080 * then return the message that was passed to us...
1084 match
= (_cups_message_t
*)cupsArrayFind(a
, &key
);
1086 #if defined(__APPLE__) && defined(CUPS_BUNDLEDIR)
1087 if (!match
&& cupsArrayUserData(a
))
1090 * Try looking the string up in the cups.strings dictionary...
1093 CFDictionaryRef dict
; /* cups.strings dictionary */
1094 CFStringRef cfm
, /* Message as a CF string */
1095 cfstr
; /* Localized text as a CF string */
1097 dict
= (CFDictionaryRef
)cupsArrayUserData(a
);
1098 cfm
= CFStringCreateWithCString(kCFAllocatorDefault
, m
,
1099 kCFStringEncodingUTF8
);
1100 match
= calloc(1, sizeof(_cups_message_t
));
1101 match
->id
= strdup(m
);
1102 cfstr
= cfm
? CFDictionaryGetValue(dict
, cfm
) : NULL
;
1106 char buffer
[1024]; /* Message buffer */
1108 CFStringGetCString(cfstr
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
);
1109 match
->str
= strdup(buffer
);
1111 DEBUG_printf(("1_cupsMessageLookup: Found \"%s\" as \"%s\"...",
1116 match
->str
= strdup(m
);
1118 DEBUG_printf(("1_cupsMessageLookup: Did not find \"%s\"...", m
));
1121 cupsArrayAdd(a
, match
);
1126 #endif /* __APPLE__ && CUPS_BUNDLEDIR */
1128 if (match
&& match
->str
)
1129 return (match
->str
);
1137 * 'appleLangDefault()' - Get the default locale string.
1140 static const char * /* O - Locale string */
1141 appleLangDefault(void)
1143 int i
; /* Looping var */
1144 CFBundleRef bundle
; /* Main bundle (if any) */
1145 CFArrayRef bundleList
; /* List of localizations in bundle */
1146 CFPropertyListRef localizationList
;
1147 /* List of localization data */
1148 CFStringRef languageName
; /* Current name */
1149 CFStringRef localeName
; /* Canonical from of name */
1150 char *lang
; /* LANG environment variable */
1151 _cups_globals_t
*cg
= _cupsGlobals();
1152 /* Pointer to library globals */
1155 DEBUG_puts("2appleLangDefault()");
1158 * Only do the lookup and translation the first time.
1161 if (!cg
->language
[0])
1163 if (getenv("SOFTWARE") != NULL
&& (lang
= getenv("LANG")) != NULL
)
1165 strlcpy(cg
->language
, lang
, sizeof(cg
->language
));
1166 return (cg
->language
);
1168 else if ((bundle
= CFBundleGetMainBundle()) != NULL
&&
1169 (bundleList
= CFBundleCopyBundleLocalizations(bundle
)) != NULL
)
1172 CFBundleCopyPreferredLocalizationsFromArray(bundleList
);
1174 CFRelease(bundleList
);
1178 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1179 kCFPreferencesCurrentApplication
);
1181 if (localizationList
)
1183 if (CFGetTypeID(localizationList
) == CFArrayGetTypeID() &&
1184 CFArrayGetCount(localizationList
) > 0)
1186 languageName
= CFArrayGetValueAtIndex(localizationList
, 0);
1189 CFGetTypeID(languageName
) == CFStringGetTypeID())
1191 localeName
= CFLocaleCreateCanonicalLocaleIdentifierFromString(
1192 kCFAllocatorDefault
, languageName
);
1196 CFStringGetCString(localeName
, cg
->language
, sizeof(cg
->language
),
1197 kCFStringEncodingASCII
);
1198 CFRelease(localeName
);
1200 DEBUG_printf(("9appleLangDefault: cg->language=\"%s\"",
1204 * Map new language identifiers to locales...
1208 i
< (int)(sizeof(apple_language_locale
) /
1209 sizeof(apple_language_locale
[0]));
1212 if (!strcmp(cg
->language
, apple_language_locale
[i
].language
))
1214 DEBUG_printf(("9appleLangDefault: mapping \"%s\" to \"%s\"...",
1215 cg
->language
, apple_language_locale
[i
].locale
));
1216 strlcpy(cg
->language
, apple_language_locale
[i
].locale
,
1217 sizeof(cg
->language
));
1223 * Convert language subtag into region subtag...
1226 if (cg
->language
[2] == '-')
1227 cg
->language
[2] = '_';
1229 if (!strchr(cg
->language
, '.'))
1230 strlcat(cg
->language
, ".UTF-8", sizeof(cg
->language
));
1235 CFRelease(localizationList
);
1239 * If we didn't find the language, default to en_US...
1242 if (!cg
->language
[0])
1243 strlcpy(cg
->language
, "en_US.UTF-8", sizeof(cg
->language
));
1247 * Return the cached locale...
1250 return (cg
->language
);
1254 # ifdef CUPS_BUNDLEDIR
1255 # ifndef CF_RETURNS_RETAINED
1256 # if __has_feature(attribute_cf_returns_retained)
1257 # define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
1259 # define CF_RETURNS_RETAINED
1260 # endif /* __has_feature(attribute_cf_returns_retained) */
1261 # endif /* !CF_RETURNED_RETAINED */
1264 * 'appleMessageLoad()' - Load a message catalog from a localizable bundle.
1267 static cups_array_t
* /* O - Message catalog */
1268 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
);
1326 CFReadStreamOpen(stream
);
1329 plist
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, stream
, 0,
1330 kCFPropertyListImmutable
, NULL
,
1334 CFStringRef msg
= CFErrorCopyDescription(error
);
1337 CFStringGetCString(msg
, filename
, sizeof(filename
),
1338 kCFStringEncodingUTF8
);
1339 DEBUG_printf(("1appleMessageLoad: %s", filename
));
1345 plist
= CFPropertyListCreateWithStream(kCFAllocatorDefault
, stream
, 0,
1346 kCFPropertyListImmutable
, NULL
,
1350 if (plist
&& CFGetTypeID(plist
) != CFDictionaryGetTypeID())
1362 DEBUG_printf(("1appleMessageLoad: url=%p, stream=%p, plist=%p", url
, stream
,
1366 * Create and return an empty array to act as a cache for messages, passing the
1367 * plist as the user data.
1370 return (cupsArrayNew3((cups_array_func_t
)cups_message_compare
, (void *)plist
,
1371 (cups_ahash_func_t
)NULL
, 0,
1372 (cups_acopy_func_t
)NULL
,
1373 (cups_afree_func_t
)cups_message_free
));
1375 # endif /* CUPS_BUNDLEDIR */
1376 #endif /* __APPLE__ */
1380 * 'cups_cache_lookup()' - Lookup a language in the cache...
1383 static cups_lang_t
* /* O - Language data or NULL */
1385 const char *name
, /* I - Name of locale */
1386 cups_encoding_t encoding
) /* I - Encoding of locale */
1388 cups_lang_t
*lang
; /* Current language */
1391 DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name
,
1392 encoding
, encoding
== CUPS_AUTO_ENCODING
? "auto" :
1393 lang_encodings
[encoding
]));
1396 * Loop through the cache and return a match if found...
1399 for (lang
= lang_cache
; lang
!= NULL
; lang
= lang
->next
)
1401 DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
1402 "encoding=%d(%s)", lang
, lang
->language
, lang
->encoding
,
1403 lang_encodings
[lang
->encoding
]));
1405 if (!strcmp(lang
->language
, name
) &&
1406 (encoding
== CUPS_AUTO_ENCODING
|| encoding
== lang
->encoding
))
1410 DEBUG_puts("8cups_cache_lookup: returning match!");
1416 DEBUG_puts("8cups_cache_lookup: returning NULL!");
1423 * 'cups_message_compare()' - Compare two messages.
1426 static int /* O - Result of comparison */
1427 cups_message_compare(
1428 _cups_message_t
*m1
, /* I - First message */
1429 _cups_message_t
*m2
) /* I - Second message */
1431 return (strcmp(m1
->id
, m2
->id
));
1436 * 'cups_message_free()' - Free a message.
1440 cups_message_free(_cups_message_t
*m
) /* I - Message */
1453 * 'cups_unquote()' - Unquote characters in strings...
1457 cups_unquote(char *d
, /* O - Unquoted string */
1458 const char *s
) /* I - Original string */
1471 *d
= *d
* 8 + *s
- '0';
1500 * End of "$Id: language.c 7558 2008-05-12 23:46:44Z mike $".