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