]>
git.ipfire.org Git - thirdparty/cups.git/blob - cups/ppd-localize.c
c20dbf937644e3c2fff5b23e75f4fd0dd57e820f
2 * PPD localization routines for CUPS.
4 * Copyright 2007-2016 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
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/".
13 * PostScript is a trademark of Adobe Systems, Inc.
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
22 * This file is subject to the Apple OS-Developed Software exception.
26 * Include necessary headers.
29 #include "cups-private.h"
30 #include "ppd-private.h"
37 static cups_lang_t
*ppd_ll_CC(char *ll_CC
, size_t ll_CC_size
);
41 * 'ppdLocalize()' - Localize the PPD file to the current locale.
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.
47 * @since CUPS 1.2/OS X 10.5@
50 int /* O - 0 on success, -1 on error */
51 ppdLocalize(ppd_file_t
*ppd
) /* I - PPD file */
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 */
59 ppd_attr_t
*attr
, /* Current attribute */
60 *locattr
; /* Localized attribute */
61 char ckeyword
[PPD_MAX_NAME
], /* Custom keyword */
62 ll_CC
[6]; /* Language + country locale */
66 * Range check input...
69 DEBUG_printf(("ppdLocalize(ppd=%p)", ppd
));
75 * Get the default language...
78 ppd_ll_CC(ll_CC
, sizeof(ll_CC
));
81 * Now lookup all of the groups, options, choices, etc.
84 for (i
= ppd
->num_groups
, group
= ppd
->groups
; i
> 0; i
--, group
++)
86 if ((locattr
= _ppdLocalizedAttr(ppd
, "Translation", group
->name
,
88 strlcpy(group
->text
, locattr
->text
, sizeof(group
->text
));
90 for (j
= group
->num_options
, option
= group
->options
; j
> 0; j
--, option
++)
92 if ((locattr
= _ppdLocalizedAttr(ppd
, "Translation", option
->keyword
,
94 strlcpy(option
->text
, locattr
->text
, sizeof(option
->text
));
96 for (k
= option
->num_choices
, choice
= option
->choices
;
100 if (strcmp(choice
->choice
, "Custom") ||
101 !ppdFindCustomOption(ppd
, option
->keyword
))
102 locattr
= _ppdLocalizedAttr(ppd
, option
->keyword
, choice
->choice
,
106 snprintf(ckeyword
, sizeof(ckeyword
), "Custom%s", option
->keyword
);
108 locattr
= _ppdLocalizedAttr(ppd
, ckeyword
, "True", ll_CC
);
112 strlcpy(choice
->text
, locattr
->text
, sizeof(choice
->text
));
118 * Translate any custom parameters...
121 for (coption
= (ppd_coption_t
*)cupsArrayFirst(ppd
->coptions
);
123 coption
= (ppd_coption_t
*)cupsArrayNext(ppd
->coptions
))
125 for (cparam
= (ppd_cparam_t
*)cupsArrayFirst(coption
->params
);
127 cparam
= (ppd_cparam_t
*)cupsArrayNext(coption
->params
))
129 snprintf(ckeyword
, sizeof(ckeyword
), "ParamCustom%s", coption
->keyword
);
131 if ((locattr
= _ppdLocalizedAttr(ppd
, ckeyword
, cparam
->name
,
133 strlcpy(cparam
->text
, locattr
->text
, sizeof(cparam
->text
));
138 * Translate ICC profile names...
141 if ((attr
= ppdFindAttr(ppd
, "APCustomColorMatchingName", NULL
)) != NULL
)
143 if ((locattr
= _ppdLocalizedAttr(ppd
, "APCustomColorMatchingName",
144 attr
->spec
, ll_CC
)) != NULL
)
145 strlcpy(attr
->text
, locattr
->text
, sizeof(attr
->text
));
148 for (attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
150 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
152 cupsArraySave(ppd
->sorted_attrs
);
154 if ((locattr
= _ppdLocalizedAttr(ppd
, "cupsICCProfile", attr
->spec
,
156 strlcpy(attr
->text
, locattr
->text
, sizeof(attr
->text
));
158 cupsArrayRestore(ppd
->sorted_attrs
);
162 * Translate printer presets...
165 for (attr
= ppdFindAttr(ppd
, "APPrinterPreset", NULL
);
167 attr
= ppdFindNextAttr(ppd
, "APPrinterPreset", NULL
))
169 cupsArraySave(ppd
->sorted_attrs
);
171 if ((locattr
= _ppdLocalizedAttr(ppd
, "APPrinterPreset", attr
->spec
,
173 strlcpy(attr
->text
, locattr
->text
, sizeof(attr
->text
));
175 cupsArrayRestore(ppd
->sorted_attrs
);
183 * 'ppdLocalizeAttr()' - Localize an attribute.
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.
190 ppd_attr_t
* /* O - Localized attribute or @code NULL@ if none exists */
191 ppdLocalizeAttr(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 */
195 ppd_attr_t
*locattr
; /* Localized attribute */
196 char ll_CC
[6]; /* Language + country locale */
200 * Get the default language...
203 ppd_ll_CC(ll_CC
, sizeof(ll_CC
));
206 * Find the localized attribute...
210 locattr
= _ppdLocalizedAttr(ppd
, keyword
, spec
, ll_CC
);
212 locattr
= _ppdLocalizedAttr(ppd
, "Translation", keyword
, ll_CC
);
215 locattr
= ppdFindAttr(ppd
, keyword
, spec
);
222 * 'ppdLocalizeIPPReason()' - Get the localized version of a cupsIPPReason
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.
230 * If no value of the requested scheme can be found, NULL is returned.
232 * @since CUPS 1.3/OS X 10.5@
235 const char * /* O - Value or NULL if not found */
236 ppdLocalizeIPPReason(
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 */
243 cups_lang_t
*lang
; /* Current language */
244 ppd_attr_t
*locattr
; /* Localized attribute */
245 char ll_CC
[6], /* Language + country locale */
246 *bufptr
, /* Pointer into buffer */
247 *bufend
, /* Pointer to end of buffer */
248 *valptr
; /* Pointer into value */
249 int ch
; /* Hex-encoded character */
250 size_t schemelen
; /* Length of scheme name */
254 * Range check input...
260 if (!ppd
|| !reason
|| (scheme
&& !*scheme
) ||
261 !buffer
|| bufsize
< PPD_MAX_TEXT
)
265 * Get the default language...
268 lang
= ppd_ll_CC(ll_CC
, sizeof(ll_CC
));
271 * Find the localized attribute...
274 if ((locattr
= _ppdLocalizedAttr(ppd
, "cupsIPPReason", reason
,
276 locattr
= ppdFindAttr(ppd
, "cupsIPPReason", reason
);
280 if (lang
&& (!scheme
|| !strcmp(scheme
, "text")))
283 * Try to localize a standard printer-state-reason keyword...
286 const char *message
= NULL
; /* Localized message */
288 if (!strncmp(reason
, "media-needed", 12))
289 message
= _("Load paper.");
290 else if (!strncmp(reason
, "media-jam", 9))
291 message
= _("Paper jam.");
292 else if (!strncmp(reason
, "offline", 7) ||
293 !strncmp(reason
, "shutdown", 8))
294 message
= _("The printer is not connected.");
295 else if (!strncmp(reason
, "toner-low", 9))
296 message
= _("The printer is low on toner.");
297 else if (!strncmp(reason
, "toner-empty", 11))
298 message
= _("The printer may be out of toner.");
299 else if (!strncmp(reason
, "cover-open", 10))
300 message
= _("The printer's cover is open.");
301 else if (!strncmp(reason
, "interlock-open", 14))
302 message
= _("The printer's interlock is open.");
303 else if (!strncmp(reason
, "door-open", 9))
304 message
= _("The printer's door is open.");
305 else if (!strncmp(reason
, "input-tray-missing", 18))
306 message
= _("Paper tray is missing.");
307 else if (!strncmp(reason
, "media-low", 9))
308 message
= _("Paper tray is almost empty.");
309 else if (!strncmp(reason
, "media-empty", 11))
310 message
= _("Paper tray is empty.");
311 else if (!strncmp(reason
, "output-tray-missing", 19))
312 message
= _("Output bin is missing.");
313 else if (!strncmp(reason
, "output-area-almost-full", 23))
314 message
= _("Output bin is almost full.");
315 else if (!strncmp(reason
, "output-area-full", 16))
316 message
= _("Output bin is full.");
317 else if (!strncmp(reason
, "marker-supply-low", 17))
318 message
= _("The printer is low on ink.");
319 else if (!strncmp(reason
, "marker-supply-empty", 19))
320 message
= _("The printer may be out of ink.");
321 else if (!strncmp(reason
, "marker-waste-almost-full", 24))
322 message
= _("The printer's waste bin is almost full.");
323 else if (!strncmp(reason
, "marker-waste-full", 17))
324 message
= _("The printer's waste bin is full.");
325 else if (!strncmp(reason
, "fuser-over-temp", 15))
326 message
= _("The fuser's temperature is high.");
327 else if (!strncmp(reason
, "fuser-under-temp", 16))
328 message
= _("The fuser's temperature is low.");
329 else if (!strncmp(reason
, "opc-near-eol", 12))
330 message
= _("The optical photoconductor will need to be replaced soon.");
331 else if (!strncmp(reason
, "opc-life-over", 13))
332 message
= _("The optical photoconductor needs to be replaced.");
333 else if (!strncmp(reason
, "developer-low", 13))
334 message
= _("The developer unit will need to be replaced soon.");
335 else if (!strncmp(reason
, "developer-empty", 15))
336 message
= _("The developer unit needs to be replaced.");
340 strlcpy(buffer
, _cupsLangString(lang
, message
), bufsize
);
349 * Now find the value we need...
352 bufend
= buffer
+ bufsize
- 1;
354 if (!scheme
|| !strcmp(scheme
, "text"))
357 * Copy a text value (either the translation text or text:... URIs from
361 strlcpy(buffer
, locattr
->text
, bufsize
);
363 for (valptr
= locattr
->value
, bufptr
= buffer
; *valptr
&& bufptr
< bufend
;)
365 if (!strncmp(valptr
, "text:", 5))
368 * Decode text: URI and add to the buffer...
373 while (*valptr
&& !_cups_isspace(*valptr
) && bufptr
< bufend
)
375 if (*valptr
== '%' && isxdigit(valptr
[1] & 255) &&
376 isxdigit(valptr
[2] & 255))
379 * Pull a hex-encoded character from the URI...
384 if (isdigit(*valptr
& 255))
385 ch
= (*valptr
- '0') << 4;
387 ch
= (tolower(*valptr
) - 'a' + 10) << 4;
390 if (isdigit(*valptr
& 255))
391 *bufptr
++ = (char)(ch
| (*valptr
- '0'));
393 *bufptr
++ = (char)(ch
| (tolower(*valptr
) - 'a' + 10));
396 else if (*valptr
== '+')
402 *bufptr
++ = *valptr
++;
411 while (*valptr
&& !_cups_isspace(*valptr
))
419 while (_cups_isspace(*valptr
))
434 schemelen
= strlen(scheme
);
435 if (scheme
[schemelen
- 1] == ':') /* Force scheme to be just the name */
438 for (valptr
= locattr
->value
, bufptr
= buffer
; *valptr
&& bufptr
< bufend
;)
440 if ((!strncmp(valptr
, scheme
, schemelen
) && valptr
[schemelen
] == ':') ||
441 (*valptr
== '/' && !strcmp(scheme
, "file")))
447 while (*valptr
&& !_cups_isspace(*valptr
) && bufptr
< bufend
)
448 *bufptr
++ = *valptr
++;
460 while (*valptr
&& !_cups_isspace(*valptr
))
468 while (_cups_isspace(*valptr
))
478 * 'ppdLocalizeMarkerName()' - Get the localized version of a marker-names
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.
485 * @since CUPS 1.4/OS X 10.6@
488 const char * /* O - Value or @code NULL@ if not found */
489 ppdLocalizeMarkerName(
490 ppd_file_t
*ppd
, /* I - PPD file */
491 const char *name
) /* I - Marker name to look up */
493 ppd_attr_t
*locattr
; /* Localized attribute */
494 char ll_CC
[6]; /* Language + country locale */
498 * Range check input...
505 * Get the default language...
508 ppd_ll_CC(ll_CC
, sizeof(ll_CC
));
511 * Find the localized attribute...
514 if ((locattr
= _ppdLocalizedAttr(ppd
, "cupsMarkerName", name
,
516 locattr
= ppdFindAttr(ppd
, "cupsMarkerName", name
);
518 return (locattr
? locattr
->text
: NULL
);
523 * '_ppdFreeLanguages()' - Free an array of languages from _ppdGetLanguages.
528 cups_array_t
*languages
) /* I - Languages array */
530 char *language
; /* Current language */
533 for (language
= (char *)cupsArrayFirst(languages
);
535 language
= (char *)cupsArrayNext(languages
))
538 cupsArrayDelete(languages
);
543 * '_ppdGetLanguages()' - Get an array of languages from a PPD file.
546 cups_array_t
* /* O - Languages array */
547 _ppdGetLanguages(ppd_file_t
*ppd
) /* I - PPD file */
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 */
557 * See if we have a cupsLanguages attribute...
560 if ((attr
= ppdFindAttr(ppd
, "cupsLanguages", NULL
)) == NULL
|| !attr
->value
)
564 * Yes, load the list...
567 if ((languages
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
)) == NULL
)
570 if ((value
= strdup(attr
->value
)) == NULL
)
572 cupsArrayDelete(languages
);
576 for (ptr
= value
; *ptr
;)
579 * Skip leading whitespace...
582 while (_cups_isspace(*ptr
))
589 * Find the end of this language name...
592 for (start
= ptr
; *ptr
&& !_cups_isspace(*ptr
); ptr
++);
597 if (!strcmp(start
, "en"))
600 cupsArrayAdd(languages
, strdup(start
));
604 * Free the temporary string and return either an array with one or more
605 * values or a NULL pointer...
610 if (cupsArrayCount(languages
) == 0)
612 cupsArrayDelete(languages
);
621 * '_ppdHashName()' - Generate a hash value for a device or profile name.
623 * This function is primarily used on OS X, but is generally accessible
624 * since cupstestppd needs to check for profile name collisions in PPD files...
627 unsigned /* O - Hash value */
628 _ppdHashName(const char *name
) /* I - Name to hash */
630 unsigned mult
, /* Multiplier */
631 hash
= 0; /* Hash value */
634 for (mult
= 1; *name
&& mult
<= 128; mult
++, name
++)
635 hash
+= (*name
& 255) * mult
;
642 * '_ppdLocalizedAttr()' - Find a localized attribute.
645 ppd_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 */
651 char lkeyword
[PPD_MAX_NAME
]; /* Localization keyword */
652 ppd_attr_t
*attr
; /* Current attribute */
655 DEBUG_printf(("4_ppdLocalizedAttr(ppd=%p, keyword=\"%s\", spec=\"%s\", "
656 "ll_CC=\"%s\")", ppd
, keyword
, spec
, ll_CC
));
659 * Look for Keyword.ll_CC, then Keyword.ll...
662 snprintf(lkeyword
, sizeof(lkeyword
), "%s.%s", ll_CC
, keyword
);
663 if ((attr
= ppdFindAttr(ppd
, lkeyword
, spec
)) == NULL
)
666 * <rdar://problem/22130168>
668 * Hong Kong locale needs special handling... Sigh...
671 if (!strcmp(ll_CC
, "zh_HK"))
673 snprintf(lkeyword
, sizeof(lkeyword
), "zh_TW.%s", keyword
);
674 attr
= ppdFindAttr(ppd
, lkeyword
, spec
);
679 snprintf(lkeyword
, sizeof(lkeyword
), "%2.2s.%s", ll_CC
, keyword
);
680 attr
= ppdFindAttr(ppd
, lkeyword
, spec
);
685 if (!strncmp(ll_CC
, "ja", 2))
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...
694 snprintf(lkeyword
, sizeof(lkeyword
), "jp.%s", keyword
);
695 attr
= ppdFindAttr(ppd
, lkeyword
, spec
);
697 else if (!strncmp(ll_CC
, "nb", 2))
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"...
705 snprintf(lkeyword
, sizeof(lkeyword
), "no.%s", keyword
);
706 attr
= ppdFindAttr(ppd
, lkeyword
, spec
);
708 else if (!strncmp(ll_CC
, "no", 2))
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...
716 snprintf(lkeyword
, sizeof(lkeyword
), "nb.%s", keyword
);
717 attr
= ppdFindAttr(ppd
, lkeyword
, spec
);
724 DEBUG_printf(("5_ppdLocalizedAttr: *%s %s/%s: \"%s\"\n", attr
->name
,
725 attr
->spec
, attr
->text
, attr
->value
? attr
->value
: ""));
727 DEBUG_puts("5_ppdLocalizedAttr: NOT FOUND");
735 * 'ppd_ll_CC()' - Get the current locale names.
738 static cups_lang_t
* /* O - Current language */
739 ppd_ll_CC(char *ll_CC
, /* O - Country-specific locale name */
740 size_t ll_CC_size
) /* I - Size of country-specific name */
742 cups_lang_t
*lang
; /* Current language */
746 * Get the current locale...
749 if ((lang
= cupsLangDefault()) == NULL
)
751 strlcpy(ll_CC
, "en_US", ll_CC_size
);
756 * Copy the locale name...
759 strlcpy(ll_CC
, lang
->language
, ll_CC_size
);
761 if (strlen(ll_CC
) == 2)
764 * Map "ll" to primary/origin country locales to have the best
765 * chance of finding a match...
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
);
780 DEBUG_printf(("8ppd_ll_CC: lang->language=\"%s\", ll_CC=\"%s\"...",
781 lang
->language
, ll_CC
));