]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/localize.c
Merge changes from CUPS 1.4svn-r8606.
[thirdparty/cups.git] / cups / localize.c
1 /*
2 * "$Id: localize.c 7679 2008-06-19 23:37:45Z mike $"
3 *
4 * PPD localization routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007-2009 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
8 *
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/".
14 *
15 * PostScript is a trademark of Adobe Systems, Inc.
16 *
17 * This code and any derivative of it may be used and distributed
18 * freely under the terms of the GNU General Public License when
19 * used with GNU Ghostscript or its derivatives. Use of the code
20 * (or any derivative of it) with software other than GNU
21 * GhostScript (or its derivatives) is governed by the CUPS license
22 * agreement.
23 *
24 * This file is subject to the Apple OS-Developed Software exception.
25 *
26 * Contents:
27 *
28 * ppdLocalize() - Localize the PPD file to the current locale.
29 * ppdLocalizeAttr() - Localize an attribute.
30 * ppdLocalizeIPPReason() - Get the localized version of a cupsIPPReason
31 * attribute.
32 * ppdLocalizeMarkerName() - Get the localized version of a marker-names
33 * attribute value.
34 * _ppdFreeLanguages() - Free an array of languages from _ppdGetLanguages.
35 * _ppdGetLanguages() - Get an array of languages from a PPD file.
36 * _ppdHashName() - Generate a hash value for a device or profile
37 * name.
38 * _ppdLocalizedAttr() - Find a localized attribute.
39 * ppd_ll_CC() - Get the current locale names.
40 */
41
42 /*
43 * Include necessary headers.
44 */
45
46 #include "globals.h"
47 #include "ppd-private.h"
48 #include "debug.h"
49
50
51 /*
52 * Local functions...
53 */
54
55 static cups_lang_t *ppd_ll_CC(char *ll_CC, int ll_CC_size);
56
57
58 /*
59 * 'ppdLocalize()' - Localize the PPD file to the current locale.
60 *
61 * All groups, options, and choices are localized, as are ICC profile
62 * descriptions, printer presets, and custom option parameters. Each
63 * localized string uses the UTF-8 character encoding.
64 *
65 * @since CUPS 1.2/Mac OS X 10.5@
66 */
67
68 int /* O - 0 on success, -1 on error */
69 ppdLocalize(ppd_file_t *ppd) /* I - PPD file */
70 {
71 int i, j, k; /* Looping vars */
72 ppd_group_t *group; /* Current group */
73 ppd_option_t *option; /* Current option */
74 ppd_choice_t *choice; /* Current choice */
75 ppd_coption_t *coption; /* Current custom option */
76 ppd_cparam_t *cparam; /* Current custom parameter */
77 ppd_attr_t *attr, /* Current attribute */
78 *locattr; /* Localized attribute */
79 char ckeyword[PPD_MAX_NAME], /* Custom keyword */
80 ll_CC[6]; /* Language + country locale */
81
82
83 /*
84 * Range check input...
85 */
86
87 DEBUG_printf(("ppdLocalize(ppd=%p)", ppd));
88
89 if (!ppd)
90 return (-1);
91
92 /*
93 * Get the default language...
94 */
95
96 ppd_ll_CC(ll_CC, sizeof(ll_CC));
97
98 /*
99 * Now lookup all of the groups, options, choices, etc.
100 */
101
102 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
103 {
104 if ((locattr = _ppdLocalizedAttr(ppd, "Translation", group->name,
105 ll_CC)) != NULL)
106 strlcpy(group->text, locattr->text, sizeof(group->text));
107
108 for (j = group->num_options, option = group->options; j > 0; j --, option ++)
109 {
110 if ((locattr = _ppdLocalizedAttr(ppd, "Translation", option->keyword,
111 ll_CC)) != NULL)
112 strlcpy(option->text, locattr->text, sizeof(option->text));
113
114 for (k = option->num_choices, choice = option->choices;
115 k > 0;
116 k --, choice ++)
117 {
118 if (strcmp(choice->choice, "Custom") ||
119 !ppdFindCustomOption(ppd, option->keyword))
120 locattr = _ppdLocalizedAttr(ppd, option->keyword, choice->choice,
121 ll_CC);
122 else
123 {
124 snprintf(ckeyword, sizeof(ckeyword), "Custom%s", option->keyword);
125
126 locattr = _ppdLocalizedAttr(ppd, ckeyword, "True", ll_CC);
127 }
128
129 if (locattr)
130 strlcpy(choice->text, locattr->text, sizeof(choice->text));
131 }
132 }
133 }
134
135 /*
136 * Translate any custom parameters...
137 */
138
139 for (coption = (ppd_coption_t *)cupsArrayFirst(ppd->coptions);
140 coption;
141 coption = (ppd_coption_t *)cupsArrayNext(ppd->coptions))
142 {
143 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
144 cparam;
145 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
146 {
147 snprintf(ckeyword, sizeof(ckeyword), "ParamCustom%s", coption->keyword);
148
149 if ((locattr = _ppdLocalizedAttr(ppd, ckeyword, cparam->name,
150 ll_CC)) != NULL)
151 strlcpy(cparam->text, locattr->text, sizeof(cparam->text));
152 }
153 }
154
155 /*
156 * Translate ICC profile names...
157 */
158
159 if ((attr = ppdFindAttr(ppd, "APCustomColorMatchingName", NULL)) != NULL)
160 {
161 if ((locattr = _ppdLocalizedAttr(ppd, "APCustomColorMatchingName",
162 attr->spec, ll_CC)) != NULL)
163 strlcpy(attr->text, locattr->text, sizeof(attr->text));
164 }
165
166 for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
167 attr;
168 attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
169 {
170 cupsArraySave(ppd->sorted_attrs);
171
172 if ((locattr = _ppdLocalizedAttr(ppd, "cupsICCProfile", attr->spec,
173 ll_CC)) != NULL)
174 strlcpy(attr->text, locattr->text, sizeof(attr->text));
175
176 cupsArrayRestore(ppd->sorted_attrs);
177 }
178
179 /*
180 * Translate printer presets...
181 */
182
183 for (attr = ppdFindAttr(ppd, "APPrinterPreset", NULL);
184 attr;
185 attr = ppdFindNextAttr(ppd, "APPrinterPreset", NULL))
186 {
187 cupsArraySave(ppd->sorted_attrs);
188
189 if ((locattr = _ppdLocalizedAttr(ppd, "APPrinterPreset", attr->spec,
190 ll_CC)) != NULL)
191 strlcpy(attr->text, locattr->text, sizeof(attr->text));
192
193 cupsArrayRestore(ppd->sorted_attrs);
194 }
195
196 return (0);
197 }
198
199
200 /*
201 * 'ppdLocalizeAttr()' - Localize an attribute.
202 *
203 * This function uses the current locale to find the localized attribute for
204 * the given main and option keywords. If no localized version of the
205 * attribute exists for the current locale, the unlocalized version is returned.
206 */
207
208 ppd_attr_t * /* O - Localized attribute or @code NULL@ if none exists */
209 ppdLocalizeAttr(ppd_file_t *ppd, /* I - PPD file */
210 const char *keyword, /* I - Main keyword */
211 const char *spec) /* I - Option keyword or @code NULL@ for none */
212 {
213 ppd_attr_t *locattr; /* Localized attribute */
214 char ll_CC[6]; /* Language + country locale */
215
216
217 /*
218 * Get the default language...
219 */
220
221 ppd_ll_CC(ll_CC, sizeof(ll_CC));
222
223 /*
224 * Find the localized attribute...
225 */
226
227 if (spec)
228 locattr = _ppdLocalizedAttr(ppd, keyword, spec, ll_CC);
229 else
230 locattr = _ppdLocalizedAttr(ppd, "Translation", keyword, ll_CC);
231
232 if (!locattr)
233 locattr = ppdFindAttr(ppd, keyword, spec);
234
235 return (locattr);
236 }
237
238
239 /*
240 * 'ppdLocalizeIPPReason()' - Get the localized version of a cupsIPPReason
241 * attribute.
242 *
243 * This function uses the current locale to find the corresponding reason
244 * text or URI from the attribute value. If "scheme" is NULL or "text",
245 * the returned value contains human-readable (UTF-8) text from the translation
246 * string or attribute value. Otherwise the corresponding URI is returned.
247 *
248 * If no value of the requested scheme can be found, NULL is returned.
249 *
250 * @since CUPS 1.3/Mac OS X 10.5@
251 */
252
253 const char * /* O - Value or NULL if not found */
254 ppdLocalizeIPPReason(
255 ppd_file_t *ppd, /* I - PPD file */
256 const char *reason, /* I - IPP reason keyword to look up */
257 const char *scheme, /* I - URI scheme or NULL for text */
258 char *buffer, /* I - Value buffer */
259 size_t bufsize) /* I - Size of value buffer */
260 {
261 cups_lang_t *lang; /* Current language */
262 ppd_attr_t *locattr; /* Localized attribute */
263 char ll_CC[6], /* Language + country locale */
264 *bufptr, /* Pointer into buffer */
265 *bufend, /* Pointer to end of buffer */
266 *valptr; /* Pointer into value */
267 int ch, /* Hex-encoded character */
268 schemelen; /* Length of scheme name */
269
270
271 /*
272 * Range check input...
273 */
274
275 if (buffer)
276 *buffer = '\0';
277
278 if (!ppd || !reason || (scheme && !*scheme) ||
279 !buffer || bufsize < PPD_MAX_TEXT)
280 return (NULL);
281
282 /*
283 * Get the default language...
284 */
285
286 lang = ppd_ll_CC(ll_CC, sizeof(ll_CC));
287
288 /*
289 * Find the localized attribute...
290 */
291
292 if ((locattr = _ppdLocalizedAttr(ppd, "cupsIPPReason", reason,
293 ll_CC)) == NULL)
294 locattr = ppdFindAttr(ppd, "cupsIPPReason", reason);
295
296 if (!locattr)
297 {
298 if (lang && (!scheme || !strcmp(scheme, "text")))
299 {
300 /*
301 * Try to localize a standard printer-state-reason keyword...
302 */
303
304 const char *message = NULL; /* Localized message */
305
306
307 if (!strncmp(reason, "media-needed", 12))
308 message = _("Media tray needs to be filled.");
309 else if (!strncmp(reason, "media-jam", 9))
310 message = _("Media jam!");
311 else if (!strncmp(reason, "moving-to-paused", 16) ||
312 !strncmp(reason, "offline", 7) ||
313 !strncmp(reason, "paused", 6) ||
314 !strncmp(reason, "shutdown", 8))
315 message = _("Printer offline.");
316 else if (!strncmp(reason, "toner-low", 9))
317 message = _("Toner low.");
318 else if (!strncmp(reason, "toner-empty", 11))
319 message = _("Out of toner!");
320 else if (!strncmp(reason, "cover-open", 10))
321 message = _("Cover open.");
322 else if (!strncmp(reason, "interlock-open", 14))
323 message = _("Interlock open.");
324 else if (!strncmp(reason, "door-open", 9))
325 message = _("Door open.");
326 else if (!strncmp(reason, "input-tray-missing", 18))
327 message = _("Media tray missing!");
328 else if (!strncmp(reason, "media-low", 9))
329 message = _("Media tray almost empty.");
330 else if (!strncmp(reason, "media-empty", 11))
331 message = _("Media tray empty!");
332 else if (!strncmp(reason, "output-tray-missing", 19))
333 message = _("Output tray missing!");
334 else if (!strncmp(reason, "output-area-almost-full", 23))
335 message = _("Output bin almost full.");
336 else if (!strncmp(reason, "output-area-full", 16))
337 message = _("Output bin full!");
338 else if (!strncmp(reason, "marker-supply-low", 17))
339 message = _("Ink/toner almost empty.");
340 else if (!strncmp(reason, "marker-supply-empty", 19))
341 message = _("Ink/toner empty!");
342 else if (!strncmp(reason, "marker-waste-almost-full", 24))
343 message = _("Ink/toner waste bin almost full.");
344 else if (!strncmp(reason, "marker-waste-full", 17))
345 message = _("Ink/toner waste bin full!");
346 else if (!strncmp(reason, "fuser-over-temp", 15))
347 message = _("Fuser temperature high!");
348 else if (!strncmp(reason, "fuser-under-temp", 16))
349 message = _("Fuser temperature low!");
350 else if (!strncmp(reason, "opc-near-eol", 12))
351 message = _("OPC almost at end-of-life.");
352 else if (!strncmp(reason, "opc-life-over", 13))
353 message = _("OPC at end-of-life!");
354 else if (!strncmp(reason, "developer-low", 13))
355 message = _("Developer almost empty.");
356 else if (!strncmp(reason, "developer-empty", 15))
357 message = _("Developer empty!");
358
359 if (message)
360 {
361 strlcpy(buffer, _cupsLangString(lang, message), bufsize);
362 return (buffer);
363 }
364 }
365
366 return (NULL);
367 }
368
369 /*
370 * Now find the value we need...
371 */
372
373 bufend = buffer + bufsize - 1;
374
375 if (!scheme || !strcmp(scheme, "text"))
376 {
377 /*
378 * Copy a text value (either the translation text or text:... URIs from
379 * the value...
380 */
381
382 strlcpy(buffer, locattr->text, bufsize);
383
384 for (valptr = locattr->value, bufptr = buffer; *valptr && bufptr < bufend;)
385 {
386 if (!strncmp(valptr, "text:", 5))
387 {
388 /*
389 * Decode text: URI and add to the buffer...
390 */
391
392 valptr += 5;
393
394 while (*valptr && !isspace(*valptr & 255) && bufptr < bufend)
395 {
396 if (*valptr == '%' && isxdigit(valptr[1] & 255) &&
397 isxdigit(valptr[2] & 255))
398 {
399 /*
400 * Pull a hex-encoded character from the URI...
401 */
402
403 valptr ++;
404
405 if (isdigit(*valptr & 255))
406 ch = (*valptr - '0') << 4;
407 else
408 ch = (tolower(*valptr) - 'a' + 10) << 4;
409 valptr ++;
410
411 if (isdigit(*valptr & 255))
412 *bufptr++ = ch | (*valptr - '0');
413 else
414 *bufptr++ = ch | (tolower(*valptr) - 'a' + 10);
415 valptr ++;
416 }
417 else if (*valptr == '+')
418 {
419 *bufptr++ = ' ';
420 valptr ++;
421 }
422 else
423 *bufptr++ = *valptr++;
424 }
425 }
426 else
427 {
428 /*
429 * Skip this URI...
430 */
431
432 while (*valptr && !isspace(*valptr & 255))
433 valptr++;
434 }
435
436 /*
437 * Skip whitespace...
438 */
439
440 while (isspace(*valptr & 255))
441 valptr ++;
442 }
443
444 if (bufptr > buffer)
445 *bufptr = '\0';
446
447 return (buffer);
448 }
449 else
450 {
451 /*
452 * Copy a URI...
453 */
454
455 schemelen = strlen(scheme);
456 if (scheme[schemelen - 1] == ':') /* Force scheme to be just the name */
457 schemelen --;
458
459 for (valptr = locattr->value, bufptr = buffer; *valptr && bufptr < bufend;)
460 {
461 if ((!strncmp(valptr, scheme, schemelen) && valptr[schemelen] == ':') ||
462 (*valptr == '/' && !strcmp(scheme, "file")))
463 {
464 /*
465 * Copy URI...
466 */
467
468 while (*valptr && !isspace(*valptr & 255) && bufptr < bufend)
469 *bufptr++ = *valptr++;
470
471 *bufptr = '\0';
472
473 return (buffer);
474 }
475 else
476 {
477 /*
478 * Skip this URI...
479 */
480
481 while (*valptr && !isspace(*valptr & 255))
482 valptr++;
483 }
484
485 /*
486 * Skip whitespace...
487 */
488
489 while (isspace(*valptr & 255))
490 valptr ++;
491 }
492
493 return (NULL);
494 }
495 }
496
497
498 /*
499 * 'ppdLocalizeMarkerName()' - Get the localized version of a marker-names
500 * attribute value.
501 *
502 * This function uses the current locale to find the corresponding name
503 * text from the attribute value. If no localized text for the requested
504 * name can be found, @code NULL@ is returned.
505 *
506 * @since CUPS 1.4@
507 */
508
509 const char * /* O - Value or @code NULL@ if not found */
510 ppdLocalizeMarkerName(
511 ppd_file_t *ppd, /* I - PPD file */
512 const char *name) /* I - Marker name to look up */
513 {
514 ppd_attr_t *locattr; /* Localized attribute */
515 char ll_CC[6]; /* Language + country locale */
516
517
518 /*
519 * Range check input...
520 */
521
522 if (!ppd || !name)
523 return (NULL);
524
525 /*
526 * Get the default language...
527 */
528
529 ppd_ll_CC(ll_CC, sizeof(ll_CC));
530
531 /*
532 * Find the localized attribute...
533 */
534
535 if ((locattr = _ppdLocalizedAttr(ppd, "cupsMarkerName", name,
536 ll_CC)) == NULL)
537 locattr = ppdFindAttr(ppd, "cupsMarkerName", name);
538
539 return (locattr ? locattr->text : NULL);
540 }
541
542
543 /*
544 * '_ppdFreeLanguages()' - Free an array of languages from _ppdGetLanguages.
545 */
546
547 void
548 _ppdFreeLanguages(
549 cups_array_t *languages) /* I - Languages array */
550 {
551 char *language; /* Current language */
552
553
554 for (language = (char *)cupsArrayFirst(languages);
555 language;
556 language = (char *)cupsArrayNext(languages))
557 free(language);
558
559 cupsArrayDelete(languages);
560 }
561
562
563 /*
564 * '_ppdGetLanguages()' - Get an array of languages from a PPD file.
565 */
566
567 cups_array_t * /* O - Languages array */
568 _ppdGetLanguages(ppd_file_t *ppd) /* I - PPD file */
569 {
570 cups_array_t *languages; /* Languages array */
571 ppd_attr_t *attr; /* cupsLanguages attribute */
572 char *value, /* Copy of attribute value */
573 *start, /* Start of current language */
574 *ptr; /* Pointer into languages */
575
576
577 /*
578 * See if we have a cupsLanguages attribute...
579 */
580
581 if ((attr = ppdFindAttr(ppd, "cupsLanguages", NULL)) == NULL || !attr->value)
582 return (NULL);
583
584 /*
585 * Yes, load the list...
586 */
587
588 if ((languages = cupsArrayNew((cups_array_func_t)strcmp, NULL)) == NULL)
589 return (NULL);
590
591 if ((value = strdup(attr->value)) == NULL)
592 {
593 cupsArrayDelete(languages);
594 return (NULL);
595 }
596
597 for (ptr = value; *ptr;)
598 {
599 /*
600 * Skip leading whitespace...
601 */
602
603 while (isspace(*ptr & 255))
604 ptr ++;
605
606 if (!*ptr)
607 break;
608
609 /*
610 * Find the end of this language name...
611 */
612
613 for (start = ptr; *ptr && !isspace(*ptr & 255); ptr ++);
614
615 if (*ptr)
616 *ptr++ = '\0';
617
618 if (!strcmp(start, "en"))
619 continue;
620
621 cupsArrayAdd(languages, strdup(start));
622 }
623
624 /*
625 * Free the temporary string and return either an array with one or more
626 * values or a NULL pointer...
627 */
628
629 free(value);
630
631 if (cupsArrayCount(languages) == 0)
632 {
633 cupsArrayDelete(languages);
634 return (NULL);
635 }
636 else
637 return (languages);
638 }
639
640
641 /*
642 * '_ppdHashName()' - Generate a hash value for a device or profile name.
643 *
644 * This function is primarily used on Mac OS X, but is generally accessible
645 * since cupstestppd needs to check for profile name collisions in PPD files...
646 */
647
648 unsigned /* O - Hash value */
649 _ppdHashName(const char *name) /* I - Name to hash */
650 {
651 int mult; /* Multiplier */
652 unsigned hash = 0; /* Hash value */
653
654
655 for (mult = 1; *name && mult <= 128; mult ++, name ++)
656 hash += (*name & 255) * mult;
657
658 return (hash);
659 }
660
661
662 /*
663 * '_ppdLocalizedAttr()' - Find a localized attribute.
664 */
665
666 ppd_attr_t * /* O - Localized attribute or NULL */
667 _ppdLocalizedAttr(ppd_file_t *ppd, /* I - PPD file */
668 const char *keyword, /* I - Main keyword */
669 const char *spec, /* I - Option keyword */
670 const char *ll_CC) /* I - Language + country locale */
671 {
672 char lkeyword[PPD_MAX_NAME]; /* Localization keyword */
673 ppd_attr_t *attr; /* Current attribute */
674
675
676 DEBUG_printf(("4_ppdLocalizedAttr(ppd=%p, keyword=\"%s\", spec=\"%s\", "
677 "ll_CC=\"%s\")", ppd, keyword, spec, ll_CC));
678
679 /*
680 * Look for Keyword.ll_CC, then Keyword.ll...
681 */
682
683 snprintf(lkeyword, sizeof(lkeyword), "%s.%s", ll_CC, keyword);
684 if ((attr = ppdFindAttr(ppd, lkeyword, spec)) == NULL)
685 {
686 snprintf(lkeyword, sizeof(lkeyword), "%2.2s.%s", ll_CC, keyword);
687 attr = ppdFindAttr(ppd, lkeyword, spec);
688
689 if (!attr)
690 {
691 if (!strncmp(ll_CC, "ja", 2))
692 {
693 /*
694 * Due to a bug in the CUPS DDK 1.1.0 ppdmerge program, Japanese
695 * PPD files were incorrectly assigned "jp" as the locale name
696 * instead of "ja". Support both the old (incorrect) and new
697 * locale names for Japanese...
698 */
699
700 snprintf(lkeyword, sizeof(lkeyword), "jp.%s", keyword);
701 attr = ppdFindAttr(ppd, lkeyword, spec);
702 }
703 else if (!strncmp(ll_CC, "no", 2))
704 {
705 /*
706 * Norway has two languages, "Bokmal" (the primary one)
707 * and "Nynorsk" (new Norwegian); we map "no" to "nb" here as
708 * recommended by the locale folks...
709 */
710
711 snprintf(lkeyword, sizeof(lkeyword), "nb.%s", keyword);
712 attr = ppdFindAttr(ppd, lkeyword, spec);
713 }
714 }
715 }
716
717 #ifdef DEBUG
718 if (attr)
719 DEBUG_printf(("5_ppdLocalizedAttr: *%s %s/%s: \"%s\"\n", attr->name,
720 attr->spec, attr->text, attr->value ? attr->value : ""));
721 else
722 DEBUG_puts("5_ppdLocalizedAttr: NOT FOUND");
723 #endif /* DEBUG */
724
725 return (attr);
726 }
727
728
729 /*
730 * 'ppd_ll_CC()' - Get the current locale names.
731 */
732
733 static cups_lang_t * /* O - Current language */
734 ppd_ll_CC(char *ll_CC, /* O - Country-specific locale name */
735 int ll_CC_size) /* I - Size of country-specific name */
736 {
737 cups_lang_t *lang; /* Current language */
738
739
740 /*
741 * Get the current locale...
742 */
743
744 if ((lang = cupsLangDefault()) == NULL)
745 {
746 strlcpy(ll_CC, "en_US", ll_CC_size);
747 return (NULL);
748 }
749
750 /*
751 * Copy the locale name...
752 */
753
754 strlcpy(ll_CC, lang->language, ll_CC_size);
755
756 if (strlen(ll_CC) == 2)
757 {
758 /*
759 * Map "ll" to primary/origin country locales to have the best
760 * chance of finding a match...
761 */
762
763 if (!strcmp(ll_CC, "cs"))
764 strlcpy(ll_CC, "cs_CZ", ll_CC_size);
765 else if (!strcmp(ll_CC, "en"))
766 strlcpy(ll_CC, "en_US", ll_CC_size);
767 else if (!strcmp(ll_CC, "ja"))
768 strlcpy(ll_CC, "ja_JP", ll_CC_size);
769 else if (!strcmp(ll_CC, "sv"))
770 strlcpy(ll_CC, "sv_SE", ll_CC_size);
771 else if (!strcmp(ll_CC, "zh")) /* Simplified Chinese */
772 strlcpy(ll_CC, "zh_CN", ll_CC_size);
773 }
774
775 DEBUG_printf(("8ppd_ll_CC: lang->language=\"%s\", ll_CC=\"%s\"...",
776 lang->language, ll_CC));
777 return (lang);
778 }
779
780
781 /*
782 * End of "$Id: localize.c 7679 2008-06-19 23:37:45Z mike $".
783 */