]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/language.c
Merge changes from CUPS 1.5svn-r9136.
[thirdparty/cups.git] / cups / language.c
CommitLineData
ef416fc2 1/*
75bd9771 2 * "$Id: language.c 7558 2008-05-12 23:46:44Z mike $"
ef416fc2 3 *
71e16022 4 * I18N/language support for CUPS.
ef416fc2 5 *
71e16022 6 * Copyright 2007-2010 by Apple Inc.
b86bc4cf 7 * Copyright 1997-2007 by Easy Software Products.
ef416fc2 8 *
9 * These coded instructions, statements, and computer programs are the
bc44d920 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/".
ef416fc2 14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
0a682745
MS
19 * _cupsAppleLanguage() - Get the Apple language identifier associated
20 * with a locale ID.
ef416fc2 21 * _cupsEncodingName() - Return the character encoding name string
22 * for the 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.
ef416fc2 33 * appleLangDefault() - Get the default locale string.
34 * cups_cache_lookup() - Lookup a language in the cache...
35 * cups_message_compare() - Compare two messages.
36 * cups_unquote() - Unquote characters in strings...
37 */
38
39/*
40 * Include necessary headers...
41 */
42
71e16022 43#include "cups-private.h"
ef416fc2 44#ifdef HAVE_LANGINFO_H
45# include <langinfo.h>
46#endif /* HAVE_LANGINFO_H */
47#ifdef WIN32
48# include <io.h>
49#else
50# include <unistd.h>
51#endif /* WIN32 */
fa73b229 52#ifdef HAVE_COREFOUNDATION_H
53# include <CoreFoundation/CoreFoundation.h>
54#endif /* HAVE_COREFOUNDATION_H */
ef416fc2 55
56
d6ae789d 57/*
58 * Local globals...
59 */
60
6d2f911b 61static _cups_mutex_t lang_mutex = _CUPS_MUTEX_INITIALIZER;
d6ae789d 62 /* Mutex to control access to cache */
d6ae789d 63static cups_lang_t *lang_cache = NULL;
64 /* Language string cache */
ef416fc2 65static const char * const lang_encodings[] =
66 { /* Encoding strings */
67 "us-ascii", "iso-8859-1",
68 "iso-8859-2", "iso-8859-3",
69 "iso-8859-4", "iso-8859-5",
70 "iso-8859-6", "iso-8859-7",
71 "iso-8859-8", "iso-8859-9",
72 "iso-8859-10", "utf-8",
73 "iso-8859-13", "iso-8859-14",
cc754834
MS
74 "iso-8859-15", "cp874",
75 "cp1250", "cp1251",
76 "cp1252", "cp1253",
77 "cp1254", "cp1255",
78 "cp1256", "cp1257",
79 "cp1258", "koi8-r",
ef416fc2 80 "koi8-u", "iso-8859-11",
bd7854cb 81 "iso-8859-16", "mac-roman",
ef416fc2 82 "unknown", "unknown",
83 "unknown", "unknown",
84 "unknown", "unknown",
85 "unknown", "unknown",
86 "unknown", "unknown",
87 "unknown", "unknown",
88 "unknown", "unknown",
89 "unknown", "unknown",
90 "unknown", "unknown",
91 "unknown", "unknown",
92 "unknown", "unknown",
93 "unknown", "unknown",
94 "unknown", "unknown",
95 "unknown", "unknown",
96 "unknown", "unknown",
97 "unknown", "unknown",
98 "unknown", "unknown",
cc754834
MS
99 "cp932", "cp936",
100 "cp949", "cp950",
101 "cp1361", "unknown",
ef416fc2 102 "unknown", "unknown",
103 "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 "euc-cn", "euc-jp",
4b3f67ff
MS
132 "euc-kr", "euc-tw",
133 "jis-x0213"
ef416fc2 134 };
135
0a682745
MS
136#ifdef __APPLE__
137typedef struct
138{
139 const char * const language; /* Language ID */
140 const char * const locale; /* Locale ID */
141} _apple_language_locale_t;
142
143static const _apple_language_locale_t apple_language_locale[] =
144{ /* Locale to language ID LUT */
145 { "en" , "en_US" },
146 { "nb" , "no" },
147 { "zh-Hans" , "zh_CN" },
148 { "zh-Hant" , "zh_TW" }
149};
150#endif /* __APPLE__ */
151
152
153/*
154 * Local functions...
155 */
156
157#ifdef __APPLE__
158static const char *appleLangDefault(void);
159#endif /* __APPLE__ */
160static cups_lang_t *cups_cache_lookup(const char *name,
161 cups_encoding_t encoding);
162static int cups_message_compare(_cups_message_t *m1,
163 _cups_message_t *m2);
164static void cups_unquote(char *d, const char *s);
165
166
167#ifdef __APPLE__
168/*
169 * _cupsAppleLanguage() - Get the Apple language identifier associated
170 * with a locale ID.
171 */
172
173const char * /* O - Language ID */
174_cupsAppleLanguage(const char *locale, /* I - Locale ID */
175 char *language,/* I - Language ID buffer */
176 size_t langsize) /* I - Size of language ID buffer */
177{
178 int i; /* Looping var */
179 CFStringRef localeid, /* CF locale identifier */
180 langid; /* CF language identifier */
181
182
183 /*
184 * Copy the locale name and convert, as needed, to the Apple-specific
185 * locale identifier...
186 */
187
188 switch (strlen(locale))
189 {
190 default :
191 /*
192 * Invalid locale...
193 */
194
195 strlcpy(language, "en", langsize);
196 break;
197
198 case 2 :
199 strlcpy(language, locale, langsize);
200 break;
201
202 case 5 :
203 strlcpy(language, locale, langsize);
204
205 if (language[2] == '-')
206 {
207 /*
208 * Convert ll-cc to ll_CC...
209 */
210
211 language[2] = '_';
212 language[3] = toupper(language[3] & 255);
213 language[4] = toupper(language[4] & 255);
214 }
215 break;
216 }
217
218 for (i = 0;
219 i < (int)(sizeof(apple_language_locale) /
220 sizeof(apple_language_locale[0]));
221 i ++)
222 if (!strcmp(locale, apple_language_locale[i].locale))
223 {
224 strlcpy(language, apple_language_locale[i].language, sizeof(language));
225 break;
226 }
227
228 /*
229 * Attempt to map the locale ID to a language ID...
230 */
231
232 if ((localeid = CFStringCreateWithCString(kCFAllocatorDefault, language,
233 kCFStringEncodingASCII)) != NULL)
234 {
235 if ((langid = CFLocaleCreateCanonicalLanguageIdentifierFromString(
236 kCFAllocatorDefault, localeid)) != NULL)
237 {
238 CFStringGetCString(langid, language, langsize, kCFStringEncodingASCII);
239 CFRelease(langid);
240 }
241
242 CFRelease(localeid);
243 }
244
245 /*
246 * Return what we got...
247 */
248
249 return (language);
250}
251#endif /* __APPLE__ */
252
ef416fc2 253
254/*
255 * '_cupsEncodingName()' - Return the character encoding name string
256 * for the given encoding enumeration.
257 */
258
259const char * /* O - Character encoding */
260_cupsEncodingName(
261 cups_encoding_t encoding) /* I - Encoding value */
262{
263 if (encoding < 0 ||
264 encoding >= (sizeof(lang_encodings) / sizeof(const char *)))
265 return (lang_encodings[0]);
266 else
267 return (lang_encodings[encoding]);
268}
269
270
271/*
272 * 'cupsLangDefault()' - Return the default language.
273 */
274
275cups_lang_t * /* O - Language data */
276cupsLangDefault(void)
277{
278 return (cupsLangGet(NULL));
279}
280
281
282/*
283 * 'cupsLangEncoding()' - Return the character encoding (us-ascii, etc.)
284 * for the given language.
285 */
286
287const char * /* O - Character encoding */
288cupsLangEncoding(cups_lang_t *lang) /* I - Language data */
289{
290 if (lang == NULL)
291 return ((char*)lang_encodings[0]);
292 else
293 return ((char*)lang_encodings[lang->encoding]);
294}
295
296
297/*
298 * 'cupsLangFlush()' - Flush all language data out of the cache.
299 */
300
301void
302cupsLangFlush(void)
303{
304 cups_lang_t *lang, /* Current language */
305 *next; /* Next language */
ef416fc2 306
307
308 /*
309 * Free all languages in the cache...
310 */
311
6d2f911b 312 _cupsMutexLock(&lang_mutex);
d6ae789d 313
314 for (lang = lang_cache; lang != NULL; lang = next)
ef416fc2 315 {
316 /*
317 * Free all messages...
318 */
319
320 _cupsMessageFree(lang->strings);
321
322 /*
323 * Then free the language structure itself...
324 */
325
326 next = lang->next;
327 free(lang);
328 }
329
d6ae789d 330 lang_cache = NULL;
331
6d2f911b 332 _cupsMutexUnlock(&lang_mutex);
ef416fc2 333}
334
335
336/*
337 * 'cupsLangFree()' - Free language data.
338 *
5a738aea 339 * This does not actually free anything; use @link cupsLangFlush@ for that.
ef416fc2 340 */
341
342void
343cupsLangFree(cups_lang_t *lang) /* I - Language to free */
344{
6d2f911b 345 _cupsMutexLock(&lang_mutex);
d6ae789d 346
ef416fc2 347 if (lang != NULL && lang->used > 0)
348 lang->used --;
d6ae789d 349
6d2f911b 350 _cupsMutexUnlock(&lang_mutex);
ef416fc2 351}
352
353
354/*
355 * 'cupsLangGet()' - Get a language.
356 */
357
358cups_lang_t * /* O - Language data */
359cupsLangGet(const char *language) /* I - Language or locale */
360{
361 int i; /* Looping var */
480ef0fe 362#ifndef __APPLE__
363 char locale[255]; /* Copy of locale name */
364#endif /* !__APPLE__ */
365 char langname[16], /* Requested language name */
ef416fc2 366 country[16], /* Country code */
367 charset[16], /* Character set */
ef416fc2 368 *csptr, /* Pointer to CODESET string */
ef416fc2 369 *ptr, /* Pointer into language/charset */
370 real[48], /* Real language name */
371 filename[1024]; /* Filename for language locale file */
372 cups_encoding_t encoding; /* Encoding to use */
373 cups_lang_t *lang; /* Current language... */
ef416fc2 374 _cups_globals_t *cg = _cupsGlobals();
375 /* Pointer to library globals */
376 static const char * const locale_encodings[] =
377 { /* Locale charset names */
378 "ASCII", "ISO88591", "ISO88592", "ISO88593",
379 "ISO88594", "ISO88595", "ISO88596", "ISO88597",
380 "ISO88598", "ISO88599", "ISO885910", "UTF8",
381 "ISO885913", "ISO885914", "ISO885915", "CP874",
382 "CP1250", "CP1251", "CP1252", "CP1253",
383 "CP1254", "CP1255", "CP1256", "CP1257",
384 "CP1258", "KOI8R", "KOI8U", "ISO885911",
bd7854cb 385 "ISO885916", "MACROMAN", "", "",
ef416fc2 386
387 "", "", "", "",
388 "", "", "", "",
389 "", "", "", "",
390 "", "", "", "",
391 "", "", "", "",
392 "", "", "", "",
393 "", "", "", "",
394 "", "", "", "",
395
396 "CP932", "CP936", "CP949", "CP950",
397 "CP1361", "", "", "",
398 "", "", "", "",
399 "", "", "", "",
400 "", "", "", "",
401 "", "", "", "",
402 "", "", "", "",
403 "", "", "", "",
404
405 "", "", "", "",
406 "", "", "", "",
407 "", "", "", "",
408 "", "", "", "",
409 "", "", "", "",
410 "", "", "", "",
411 "", "", "", "",
412 "", "", "", "",
413
4b3f67ff
MS
414 "EUCCN", "EUCJP", "EUCKR", "EUCTW",
415 "SHIFT_JISX0213"
ef416fc2 416 };
417
418
f11a948a 419 DEBUG_printf(("2cupsLangGet(language=\"%s\")", language));
ef416fc2 420
421#ifdef __APPLE__
757d2cad 422 /*
423 * Set the character set to UTF-8...
424 */
425
426 strcpy(charset, "UTF8");
427
ef416fc2 428 /*
429 * Apple's setlocale doesn't give us the user's localization
430 * preference so we have to look it up this way...
431 */
432
c9fc04c6
MS
433 if (!language)
434 {
b0f6947b 435 if (!getenv("SOFTWARE") || (language = getenv("LANG")) == NULL)
c9fc04c6
MS
436 language = appleLangDefault();
437
f11a948a 438 DEBUG_printf(("4cupsLangGet: language=\"%s\"", language));
c9fc04c6 439 }
757d2cad 440
ef416fc2 441#else
757d2cad 442 /*
443 * Set the charset to "unknown"...
444 */
445
446 charset[0] = '\0';
447
448 /*
449 * Use setlocale() to determine the currently set locale, and then
450 * fallback to environment variables to avoid setting the locale,
451 * since setlocale() is not thread-safe!
452 */
453
454 if (!language)
ef416fc2 455 {
456 /*
457 * First see if the locale has been set; if it is still "C" or
757d2cad 458 * "POSIX", use the environment to get the default...
ef416fc2 459 */
460
461# ifdef LC_MESSAGES
462 ptr = setlocale(LC_MESSAGES, NULL);
463# else
464 ptr = setlocale(LC_ALL, NULL);
465# endif /* LC_MESSAGES */
466
f11a948a 467 DEBUG_printf(("4cupsLangGet: current locale is \"%s\"", ptr));
ef416fc2 468
469 if (!ptr || !strcmp(ptr, "C") || !strcmp(ptr, "POSIX"))
ef416fc2 470 {
757d2cad 471 /*
472 * Get the character set from the LC_CTYPE locale setting...
473 */
474
475 if ((ptr = getenv("LC_CTYPE")) == NULL)
476 if ((ptr = getenv("LC_ALL")) == NULL)
477 if ((ptr = getenv("LANG")) == NULL)
478 ptr = "en_US";
479
480 if ((csptr = strchr(ptr, '.')) != NULL)
481 {
482 /*
483 * Extract the character set from the environment...
484 */
485
486 for (ptr = charset, csptr ++; *csptr; csptr ++)
487 if (ptr < (charset + sizeof(charset) - 1) && isalnum(*csptr & 255))
488 *ptr++ = *csptr;
489
490 *ptr = '\0';
491 }
757d2cad 492
493 /*
494 * Get the locale for messages from the LC_MESSAGES locale setting...
495 */
496
497 if ((ptr = getenv("LC_MESSAGES")) == NULL)
498 if ((ptr = getenv("LC_ALL")) == NULL)
499 if ((ptr = getenv("LANG")) == NULL)
500 ptr = "en_US";
ef416fc2 501 }
ef416fc2 502
503 if (ptr)
504 {
505 strlcpy(locale, ptr, sizeof(locale));
506 language = locale;
507
a41f09e2
MS
508 /*
509 * CUPS STR #2575: Map "nb" to "no" for back-compatibility...
510 */
511
512 if (!strncmp(locale, "nb", 2))
513 locale[1] = 'o';
514
f11a948a 515 DEBUG_printf(("4cupsLangGet: new language value is \"%s\"", language));
ef416fc2 516 }
517 }
518#endif /* __APPLE__ */
519
520 /*
521 * If "language" is NULL at this point, then chances are we are using
522 * a language that is not installed for the base OS.
523 */
524
525 if (!language)
526 {
527 /*
757d2cad 528 * Switch to the POSIX ("C") locale...
ef416fc2 529 */
530
757d2cad 531 language = "C";
ef416fc2 532 }
533
ef416fc2 534#ifdef CODESET
535 /*
536 * On systems that support the nl_langinfo(CODESET) call, use
537 * this value as the character set...
538 */
539
757d2cad 540 if (!charset[0] && (csptr = nl_langinfo(CODESET)) != NULL)
ef416fc2 541 {
542 /*
543 * Copy all of the letters and numbers in the CODESET string...
544 */
545
546 for (ptr = charset; *csptr; csptr ++)
547 if (isalnum(*csptr & 255) && ptr < (charset + sizeof(charset) - 1))
548 *ptr++ = *csptr;
549
550 *ptr = '\0';
551
f11a948a 552 DEBUG_printf(("4cupsLangGet: charset set to \"%s\" via "
e07d4801 553 "nl_langinfo(CODESET)...", charset));
ef416fc2 554 }
555#endif /* CODESET */
556
07725fee 557 /*
558 * If we don't have a character set by now, default to UTF-8...
559 */
560
561 if (!charset[0])
562 strcpy(charset, "UTF8");
563
ef416fc2 564 /*
565 * Parse the language string passed in to a locale string. "C" is the
566 * standard POSIX locale and is copied unchanged. Otherwise the
567 * language string is converted from ll-cc[.charset] (language-country)
568 * to ll_CC[.CHARSET] to match the file naming convention used by all
569 * POSIX-compliant operating systems. Invalid language names are mapped
570 * to the POSIX locale.
571 */
572
573 country[0] = '\0';
574
575 if (language == NULL || !language[0] ||
576 !strcmp(language, "POSIX"))
577 strcpy(langname, "C");
578 else
579 {
580 /*
581 * Copy the parts of the locale string over safely...
582 */
583
584 for (ptr = langname; *language; language ++)
585 if (*language == '_' || *language == '-' || *language == '.')
586 break;
587 else if (ptr < (langname + sizeof(langname) - 1))
588 *ptr++ = tolower(*language & 255);
589
590 *ptr = '\0';
591
592 if (*language == '_' || *language == '-')
593 {
594 /*
595 * Copy the country code...
596 */
597
598 for (language ++, ptr = country; *language; language ++)
599 if (*language == '.')
600 break;
601 else if (ptr < (country + sizeof(country) - 1))
602 *ptr++ = toupper(*language & 255);
603
604 *ptr = '\0';
605 }
606
607 if (*language == '.' && !charset[0])
608 {
609 /*
610 * Copy the encoding...
611 */
612
613 for (language ++, ptr = charset; *language; language ++)
614 if (isalnum(*language & 255) && ptr < (charset + sizeof(charset) - 1))
615 *ptr++ = toupper(*language & 255);
616
617 *ptr = '\0';
618 }
619
620 /*
621 * Force a POSIX locale for an invalid language name...
622 */
623
624 if (strlen(langname) != 2)
625 {
626 strcpy(langname, "C");
627 country[0] = '\0';
628 charset[0] = '\0';
629 }
630 }
631
f11a948a 632 DEBUG_printf(("4cupsLangGet: langname=\"%s\", country=\"%s\", charset=\"%s\"",
ef416fc2 633 langname, country, charset));
634
635 /*
636 * Figure out the desired encoding...
637 */
638
639 encoding = CUPS_AUTO_ENCODING;
640
641 if (charset[0])
642 {
bd7854cb 643 for (i = 0;
644 i < (int)(sizeof(locale_encodings) / sizeof(locale_encodings[0]));
645 i ++)
ef416fc2 646 if (!strcasecmp(charset, locale_encodings[i]))
647 {
648 encoding = (cups_encoding_t)i;
649 break;
650 }
8ca02f3c 651
652 if (encoding == CUPS_AUTO_ENCODING)
653 {
654 /*
655 * Map alternate names for various character sets...
656 */
657
658 if (!strcasecmp(charset, "iso-2022-jp") ||
659 !strcasecmp(charset, "sjis"))
660 encoding = CUPS_WINDOWS_932;
661 else if (!strcasecmp(charset, "iso-2022-cn"))
662 encoding = CUPS_WINDOWS_936;
663 else if (!strcasecmp(charset, "iso-2022-kr"))
664 encoding = CUPS_WINDOWS_949;
665 else if (!strcasecmp(charset, "big5"))
666 encoding = CUPS_WINDOWS_950;
667 }
ef416fc2 668 }
669
f11a948a 670 DEBUG_printf(("4cupsLangGet: encoding=%d(%s)", encoding,
ef416fc2 671 encoding == CUPS_AUTO_ENCODING ? "auto" :
672 lang_encodings[encoding]));
673
674 /*
675 * See if we already have this language/country loaded...
676 */
677
bd7854cb 678 if (country[0])
679 {
680 snprintf(real, sizeof(real), "%s_%s", langname, country);
ef416fc2 681
bd7854cb 682 snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
683 real, real);
684 }
685 else
d6ae789d 686 {
687 strcpy(real, langname);
bd7854cb 688 filename[0] = '\0'; /* anti-compiler-warning-code */
d6ae789d 689 }
690
6d2f911b 691 _cupsMutexLock(&lang_mutex);
d6ae789d 692
c0e1af83 693 if ((lang = cups_cache_lookup(real, encoding)) != NULL)
d6ae789d 694 {
6d2f911b 695 _cupsMutexUnlock(&lang_mutex);
d6ae789d 696
f11a948a 697 DEBUG_printf(("3cupsLangGet: Using cached copy of \"%s\"...", real));
c9fc04c6 698
d6ae789d 699 return (lang);
700 }
ef416fc2 701
702 if (!country[0] || access(filename, 0))
703 {
704 /*
705 * Country localization not available, look for generic localization...
706 */
707
bd7854cb 708 snprintf(filename, sizeof(filename), "%s/%s/cups_%s.po", cg->localedir,
ef416fc2 709 langname, langname);
710
711 if (access(filename, 0))
712 {
713 /*
714 * No generic localization, so use POSIX...
715 */
716
f11a948a 717 DEBUG_printf(("4cupsLangGet: access(\"%s\", 0): %s", filename,
ae71f5de 718 strerror(errno)));
bd7854cb 719
bd7854cb 720 snprintf(filename, sizeof(filename), "%s/C/cups_C.po", cg->localedir);
ef416fc2 721 }
ef416fc2 722 }
723
724 /*
725 * See if there is a free language available; if so, use that
726 * record...
727 */
728
d6ae789d 729 for (lang = lang_cache; lang != NULL; lang = lang->next)
ef416fc2 730 if (lang->used == 0)
731 break;
732
733 if (lang == NULL)
734 {
735 /*
736 * Allocate memory for the language and add it to the cache.
737 */
738
739 if ((lang = calloc(sizeof(cups_lang_t), 1)) == NULL)
d6ae789d 740 {
6d2f911b 741 _cupsMutexUnlock(&lang_mutex);
d6ae789d 742
ef416fc2 743 return (NULL);
d6ae789d 744 }
ef416fc2 745
d6ae789d 746 lang->next = lang_cache;
747 lang_cache = lang;
ef416fc2 748 }
bd7854cb 749 else
750 {
751 /*
752 * Free all old strings as needed...
753 */
ef416fc2 754
bd7854cb 755 _cupsMessageFree(lang->strings);
756 }
ef416fc2 757
758 /*
759 * Then assign the language and encoding fields...
760 */
761
762 lang->used ++;
763 strlcpy(lang->language, real, sizeof(lang->language));
764
765 if (encoding != CUPS_AUTO_ENCODING)
766 lang->encoding = encoding;
767 else
768 lang->encoding = CUPS_UTF8;
769
770 /*
771 * Read the strings from the file...
772 */
773
8b116e60 774 lang->strings = _cupsMessageLoad(filename, 1);
ef416fc2 775
776 /*
777 * Return...
778 */
779
6d2f911b 780 _cupsMutexUnlock(&lang_mutex);
d6ae789d 781
ef416fc2 782 return (lang);
783}
784
785
786/*
787 * '_cupsLangString()' - Get a message string.
788 *
789 * The returned string is UTF-8 encoded; use cupsUTF8ToCharset() to
790 * convert the string to the language encoding.
791 */
792
793const char * /* O - Localized message */
794_cupsLangString(cups_lang_t *lang, /* I - Language */
795 const char *message) /* I - Message */
796{
6d2f911b
MS
797 const char *s; /* Localized message */
798
ef416fc2 799 /*
800 * Range check input...
801 */
802
803 if (!lang || !message)
804 return (message);
805
6d2f911b 806 _cupsMutexLock(&lang_mutex);
d6ae789d 807
6d2f911b 808 s = _cupsMessageLookup(lang->strings, message);
d6ae789d 809
6d2f911b 810 _cupsMutexUnlock(&lang_mutex);
d6ae789d 811
6d2f911b 812 return (s);
ef416fc2 813}
814
815
816/*
817 * '_cupsMessageFree()' - Free a messages array.
818 */
819
820void
821_cupsMessageFree(cups_array_t *a) /* I - Message array */
822{
823 _cups_message_t *m; /* Current message */
824
825
826 for (m = (_cups_message_t *)cupsArrayFirst(a);
827 m;
828 m = (_cups_message_t *)cupsArrayNext(a))
829 {
830 /*
831 * Remove the message from the array, then free the message and strings.
832 */
833
834 cupsArrayRemove(a, m);
835
836 if (m->id)
837 free(m->id);
838
839 if (m->str)
840 free(m->str);
841
842 free(m);
843 }
844
845 /*
846 * Free the array...
847 */
848
849 cupsArrayDelete(a);
850}
851
852
853/*
854 * '_cupsMessageLoad()' - Load a .po file into a messages array.
855 */
856
857cups_array_t * /* O - New message array */
8b116e60
MS
858_cupsMessageLoad(const char *filename, /* I - Message catalog to load */
859 int unquote) /* I - Unescape \foo in strings */
ef416fc2 860{
861 cups_file_t *fp; /* Message file */
862 cups_array_t *a; /* Message array */
863 _cups_message_t *m; /* Current message */
864 char s[4096], /* String buffer */
865 *ptr, /* Pointer into buffer */
866 *temp; /* New string */
867 int length; /* Length of combined strings */
868
869
e07d4801 870 DEBUG_printf(("4_cupsMessageLoad(filename=\"%s\")", filename));
bd7854cb 871
ef416fc2 872 /*
873 * Create an array to hold the messages...
874 */
875
876 if ((a = cupsArrayNew((cups_array_func_t)cups_message_compare, NULL)) == NULL)
c9fc04c6 877 {
e07d4801 878 DEBUG_puts("5_cupsMessageLoad: Unable to allocate array!");
ef416fc2 879 return (NULL);
c9fc04c6 880 }
ef416fc2 881
882 /*
883 * Open the message catalog file...
884 */
885
886 if ((fp = cupsFileOpen(filename, "r")) == NULL)
c9fc04c6 887 {
e07d4801 888 DEBUG_printf(("5_cupsMessageLoad: Unable to open file: %s",
c9fc04c6 889 strerror(errno)));
ef416fc2 890 return (a);
c9fc04c6 891 }
ef416fc2 892
893 /*
894 * Read messages from the catalog file until EOF...
895 *
896 * The format is the GNU gettext .po format, which is fairly simple:
897 *
898 * msgid "some text"
899 * msgstr "localized text"
900 *
bd7854cb 901 * The ID and localized text can span multiple lines using the form:
ef416fc2 902 *
bd7854cb 903 * msgid ""
904 * "some long text"
905 * msgstr ""
906 * "localized text spanning "
ef416fc2 907 * "multiple lines"
908 */
909
910 m = NULL;
911
912 while (cupsFileGets(fp, s, sizeof(s)) != NULL)
913 {
914 /*
915 * Skip blank and comment lines...
916 */
917
918 if (s[0] == '#' || !s[0])
919 continue;
920
921 /*
922 * Strip the trailing quote...
923 */
924
925 if ((ptr = strrchr(s, '\"')) == NULL)
926 continue;
927
928 *ptr = '\0';
929
930 /*
931 * Find start of value...
932 */
933
934 if ((ptr = strchr(s, '\"')) == NULL)
935 continue;
936
937 ptr ++;
938
939 /*
940 * Unquote the text...
941 */
942
8b116e60
MS
943 if (unquote)
944 cups_unquote(ptr, ptr);
ef416fc2 945
946 /*
947 * Create or add to a message...
948 */
949
950 if (!strncmp(s, "msgid", 5))
951 {
bd7854cb 952 /*
953 * Add previous message as needed...
954 */
955
956 if (m)
957 cupsArrayAdd(a, m);
958
959 /*
960 * Create a new message with the given msgid string...
961 */
962
ef416fc2 963 if ((m = (_cups_message_t *)calloc(1, sizeof(_cups_message_t))) == NULL)
964 {
965 cupsFileClose(fp);
966 return (a);
967 }
968
91c84a35
MS
969 if ((m->id = strdup(ptr)) == NULL)
970 {
971 free(m);
972 cupsFileClose(fp);
973 return (a);
974 }
ef416fc2 975 }
bd7854cb 976 else if (s[0] == '\"' && m)
ef416fc2 977 {
bd7854cb 978 /*
979 * Append to current string...
980 */
ef416fc2 981
b86bc4cf 982 length = (int)strlen(m->str ? m->str : m->id);
ef416fc2 983
bd7854cb 984 if ((temp = realloc(m->str ? m->str : m->id,
985 length + strlen(ptr) + 1)) == NULL)
986 {
987 cupsFileClose(fp);
988 return (a);
989 }
ef416fc2 990
bd7854cb 991 if (m->str)
992 {
ef416fc2 993 /*
bd7854cb 994 * Copy the new portion to the end of the msgstr string - safe
995 * to use strcpy because the buffer is allocated to the correct
996 * size...
ef416fc2 997 */
998
bd7854cb 999 m->str = temp;
1000
ef416fc2 1001 strcpy(m->str + length, ptr);
1002 }
1003 else
1004 {
1005 /*
bd7854cb 1006 * Copy the new portion to the end of the msgid string - safe
1007 * to use strcpy because the buffer is allocated to the correct
1008 * size...
ef416fc2 1009 */
1010
bd7854cb 1011 m->id = temp;
1012
1013 strcpy(m->id + length, ptr);
ef416fc2 1014 }
1015 }
bd7854cb 1016 else if (!strncmp(s, "msgstr", 6) && m)
1017 {
1018 /*
1019 * Set the string...
1020 */
1021
91c84a35
MS
1022 if ((m->str = strdup(ptr)) == NULL)
1023 {
1024 cupsFileClose(fp);
1025 return (a);
1026 }
bd7854cb 1027 }
ef416fc2 1028 }
1029
bd7854cb 1030 /*
1031 * Add the last message string to the array as needed...
1032 */
1033
1034 if (m)
1035 cupsArrayAdd(a, m);
1036
ef416fc2 1037 /*
1038 * Close the message catalog file and return the new array...
1039 */
1040
1041 cupsFileClose(fp);
1042
e07d4801 1043 DEBUG_printf(("5_cupsMessageLoad: Returning %d messages...",
c9fc04c6
MS
1044 cupsArrayCount(a)));
1045
ef416fc2 1046 return (a);
1047}
1048
1049
1050/*
1051 * '_cupsMessageLookup()' - Lookup a message string.
1052 */
1053
1054const char * /* O - Localized message */
1055_cupsMessageLookup(cups_array_t *a, /* I - Message array */
1056 const char *m) /* I - Message */
1057{
1058 _cups_message_t key, /* Search key */
1059 *match; /* Matching message */
1060
1061
1062 /*
1063 * Lookup the message string; if it doesn't exist in the catalog,
1064 * then return the message that was passed to us...
1065 */
1066
1067 key.id = (char *)m;
1068 match = (_cups_message_t *)cupsArrayFind(a, &key);
1069
1070 if (match && match->str)
1071 return (match->str);
1072 else
1073 return (m);
1074}
1075
1076
ef416fc2 1077#ifdef __APPLE__
ef416fc2 1078/*
1079 * 'appleLangDefault()' - Get the default locale string.
1080 */
1081
1082static const char * /* O - Locale string */
1083appleLangDefault(void)
1084{
b94498cf 1085 int i; /* Looping var */
db1f069b
MS
1086 CFBundleRef bundle; /* Main bundle (if any) */
1087 CFArrayRef bundleList; /* List of localizations in bundle */
ef416fc2 1088 CFPropertyListRef localizationList;
1089 /* List of localization data */
1090 CFStringRef languageName; /* Current name */
1091 CFStringRef localeName; /* Canonical from of name */
26d47ec6 1092 char *lang; /* LANG environment variable */
ef416fc2 1093 _cups_globals_t *cg = _cupsGlobals();
1094 /* Pointer to library globals */
1095
1096
b0f6947b
MS
1097 DEBUG_puts("2appleLangDefault()");
1098
ef416fc2 1099 /*
1100 * Only do the lookup and translation the first time.
1101 */
1102
1103 if (!cg->language[0])
1104 {
b0f6947b 1105 if (getenv("SOFTWARE") != NULL && (lang = getenv("LANG")) != NULL)
db1f069b 1106 {
26d47ec6 1107 strlcpy(cg->language, lang, sizeof(cg->language));
db1f069b
MS
1108 return (cg->language);
1109 }
1110 else if ((bundle = CFBundleGetMainBundle()) != NULL &&
1111 (bundleList = CFBundleCopyBundleLocalizations(bundle)) != NULL)
ef416fc2 1112 {
26d47ec6 1113 localizationList =
db1f069b 1114 CFBundleCopyPreferredLocalizationsFromArray(bundleList);
26d47ec6 1115
db1f069b
MS
1116 CFRelease(bundleList);
1117 }
1118 else
1119 localizationList =
1120 CFPreferencesCopyAppValue(CFSTR("AppleLanguages"),
1121 kCFPreferencesCurrentApplication);
1122
1123 if (localizationList)
1124 {
1125 if (CFGetTypeID(localizationList) == CFArrayGetTypeID() &&
1126 CFArrayGetCount(localizationList) > 0)
ef416fc2 1127 {
db1f069b
MS
1128 languageName = CFArrayGetValueAtIndex(localizationList, 0);
1129
1130 if (languageName &&
1131 CFGetTypeID(languageName) == CFStringGetTypeID())
26d47ec6 1132 {
db1f069b
MS
1133 localeName = CFLocaleCreateCanonicalLocaleIdentifierFromString(
1134 kCFAllocatorDefault, languageName);
ef416fc2 1135
db1f069b
MS
1136 if (localeName)
1137 {
1138 CFStringGetCString(localeName, cg->language, sizeof(cg->language),
1139 kCFStringEncodingASCII);
1140 CFRelease(localeName);
ef416fc2 1141
e07d4801 1142 DEBUG_printf(("9appleLangDefault: cg->language=\"%s\"",
db1f069b 1143 cg->language));
26d47ec6 1144
db1f069b
MS
1145 /*
1146 * Map new language identifiers to locales...
1147 */
b94498cf 1148
db1f069b 1149 for (i = 0;
0a682745
MS
1150 i < (int)(sizeof(apple_language_locale) /
1151 sizeof(apple_language_locale[0]));
1152 i ++)
db1f069b 1153 {
0a682745 1154 if (!strcmp(cg->language, apple_language_locale[i].language))
b94498cf 1155 {
e07d4801 1156 DEBUG_printf(("9appleLangDefault: mapping \"%s\" to \"%s\"...",
0a682745
MS
1157 cg->language, apple_language_locale[i].locale));
1158 strlcpy(cg->language, apple_language_locale[i].locale,
db1f069b
MS
1159 sizeof(cg->language));
1160 break;
b94498cf 1161 }
db1f069b 1162 }
b94498cf 1163
db1f069b
MS
1164 /*
1165 * Convert language subtag into region subtag...
1166 */
b94498cf 1167
db1f069b
MS
1168 if (cg->language[2] == '-')
1169 cg->language[2] = '_';
b94498cf 1170
db1f069b
MS
1171 if (!strchr(cg->language, '.'))
1172 strlcat(cg->language, ".UTF-8", sizeof(cg->language));
1173 }
26d47ec6 1174 }
26d47ec6 1175 }
db1f069b
MS
1176
1177 CFRelease(localizationList);
ef416fc2 1178 }
1179
1180 /*
1181 * If we didn't find the language, default to en_US...
1182 */
1183
1184 if (!cg->language[0])
1185 strlcpy(cg->language, "en_US.UTF-8", sizeof(cg->language));
1186 }
1187
1188 /*
1189 * Return the cached locale...
1190 */
1191
1192 return (cg->language);
1193}
ef416fc2 1194#endif /* __APPLE__ */
1195
1196
1197/*
1198 * 'cups_cache_lookup()' - Lookup a language in the cache...
1199 */
1200
1201static cups_lang_t * /* O - Language data or NULL */
1202cups_cache_lookup(const char *name,/* I - Name of locale */
1203 cups_encoding_t encoding)
1204 /* I - Encoding of locale */
1205{
1206 cups_lang_t *lang; /* Current language */
1207
1208
e07d4801 1209 DEBUG_printf(("7cups_cache_lookup(name=\"%s\", encoding=%d(%s))", name,
ef416fc2 1210 encoding, encoding == CUPS_AUTO_ENCODING ? "auto" :
1211 lang_encodings[encoding]));
1212
1213 /*
1214 * Loop through the cache and return a match if found...
1215 */
1216
d6ae789d 1217 for (lang = lang_cache; lang != NULL; lang = lang->next)
ef416fc2 1218 {
e07d4801
MS
1219 DEBUG_printf(("9cups_cache_lookup: lang=%p, language=\"%s\", "
1220 "encoding=%d(%s)", lang, lang->language, lang->encoding,
ef416fc2 1221 lang_encodings[lang->encoding]));
1222
1223 if (!strcmp(lang->language, name) &&
1224 (encoding == CUPS_AUTO_ENCODING || encoding == lang->encoding))
1225 {
1226 lang->used ++;
1227
e07d4801 1228 DEBUG_puts("8cups_cache_lookup: returning match!");
ef416fc2 1229
1230 return (lang);
1231 }
1232 }
1233
e07d4801 1234 DEBUG_puts("8cups_cache_lookup: returning NULL!");
ef416fc2 1235
1236 return (NULL);
1237}
1238
1239
1240/*
1241 * 'cups_message_compare()' - Compare two messages.
1242 */
1243
1244static int /* O - Result of comparison */
1245cups_message_compare(
1246 _cups_message_t *m1, /* I - First message */
1247 _cups_message_t *m2) /* I - Second message */
1248{
1249 return (strcmp(m1->id, m2->id));
1250}
1251
1252
1253/*
1254 * 'cups_unquote()' - Unquote characters in strings...
1255 */
1256
1257static void
1258cups_unquote(char *d, /* O - Unquoted string */
1259 const char *s) /* I - Original string */
1260{
1261 while (*s)
1262 {
1263 if (*s == '\\')
1264 {
1265 s ++;
1266 if (isdigit(*s))
1267 {
1268 *d = 0;
1269
1270 while (isdigit(*s))
1271 {
1272 *d = *d * 8 + *s - '0';
1273 s ++;
1274 }
d09495fa 1275
1276 d ++;
ef416fc2 1277 }
1278 else
1279 {
1280 if (*s == 'n')
1281 *d ++ = '\n';
1282 else if (*s == 'r')
1283 *d ++ = '\r';
1284 else if (*s == 't')
1285 *d ++ = '\t';
1286 else
1287 *d++ = *s;
1288
1289 s ++;
1290 }
1291 }
1292 else
1293 *d++ = *s++;
1294 }
1295
1296 *d = '\0';
1297}
1298
1299
1300/*
75bd9771 1301 * End of "$Id: language.c 7558 2008-05-12 23:46:44Z mike $".
ef416fc2 1302 */