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