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