1/* 2 * "$Id: localize.c 11093 2013-07-03 20:48:42Z msweet $" 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 54static 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/OS X 10.5@ 65 */ 66 67int /* O - 0 on success, -1 on error */ 68ppdLocalize(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 207ppd_attr_t * /* O - Localized attribute or @code NULL@ if none exists */ 208ppdLocalizeAttr(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/OS X 10.5@ 250 */ 251 252const char * /* O - Value or NULL if not found */ 253ppdLocalizeIPPReason( 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 may be 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/OS X 10.6@ 503 */ 504 505const char * /* O - Value or @code NULL@ if not found */ 506ppdLocalizeMarkerName( 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 543void 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 563cups_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 OS X, but is generally accessible 641 * since cupstestppd needs to check for profile name collisions in PPD files... 642 */ 643 644unsigned /* 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 662ppd_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 729static cups_lang_t * /* O - Current language */ 730ppd_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 11093 2013-07-03 20:48:42Z msweet $". 779 */ 780