]>
git.ipfire.org Git - thirdparty/cups.git/blob - cups/ppd-localize.c
2 * PPD localization routines for CUPS.
4 * Copyright 2007-2018 by Apple Inc.
5 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
7 * Licensed under Apache License v2.0. See the file "LICENSE" for more
10 * PostScript is a trademark of Adobe Systems, Inc.
14 * Include necessary headers.
17 #include "cups-private.h"
18 #include "ppd-private.h"
19 #include "debug-internal.h"
26 static cups_lang_t
*ppd_ll_CC(char *ll_CC
, size_t ll_CC_size
);
30 * 'ppdLocalize()' - Localize the PPD file to the current locale.
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.
36 * @since CUPS 1.2/macOS 10.5@
39 int /* O - 0 on success, -1 on error */
40 ppdLocalize(ppd_file_t
*ppd
) /* I - PPD file */
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 */
48 ppd_attr_t
*attr
, /* Current attribute */
49 *locattr
; /* Localized attribute */
50 char ckeyword
[PPD_MAX_NAME
], /* Custom keyword */
51 ll_CC
[6]; /* Language + country locale */
55 * Range check input...
58 DEBUG_printf(("ppdLocalize(ppd=%p)", ppd
));
64 * Get the default language...
67 ppd_ll_CC(ll_CC
, sizeof(ll_CC
));
70 * Now lookup all of the groups, options, choices, etc.
73 for (i
= ppd
->num_groups
, group
= ppd
->groups
; i
> 0; i
--, group
++)
75 if ((locattr
= _ppdLocalizedAttr(ppd
, "Translation", group
->name
,
77 strlcpy(group
->text
, locattr
->text
, sizeof(group
->text
));
79 for (j
= group
->num_options
, option
= group
->options
; j
> 0; j
--, option
++)
81 if ((locattr
= _ppdLocalizedAttr(ppd
, "Translation", option
->keyword
,
83 strlcpy(option
->text
, locattr
->text
, sizeof(option
->text
));
85 for (k
= option
->num_choices
, choice
= option
->choices
;
89 if (strcmp(choice
->choice
, "Custom") ||
90 !ppdFindCustomOption(ppd
, option
->keyword
))
91 locattr
= _ppdLocalizedAttr(ppd
, option
->keyword
, choice
->choice
,
95 snprintf(ckeyword
, sizeof(ckeyword
), "Custom%s", option
->keyword
);
97 locattr
= _ppdLocalizedAttr(ppd
, ckeyword
, "True", ll_CC
);
101 strlcpy(choice
->text
, locattr
->text
, sizeof(choice
->text
));
107 * Translate any custom parameters...
110 for (coption
= (ppd_coption_t
*)cupsArrayFirst(ppd
->coptions
);
112 coption
= (ppd_coption_t
*)cupsArrayNext(ppd
->coptions
))
114 for (cparam
= (ppd_cparam_t
*)cupsArrayFirst(coption
->params
);
116 cparam
= (ppd_cparam_t
*)cupsArrayNext(coption
->params
))
118 snprintf(ckeyword
, sizeof(ckeyword
), "ParamCustom%s", coption
->keyword
);
120 if ((locattr
= _ppdLocalizedAttr(ppd
, ckeyword
, cparam
->name
,
122 strlcpy(cparam
->text
, locattr
->text
, sizeof(cparam
->text
));
127 * Translate ICC profile names...
130 if ((attr
= ppdFindAttr(ppd
, "APCustomColorMatchingName", NULL
)) != NULL
)
132 if ((locattr
= _ppdLocalizedAttr(ppd
, "APCustomColorMatchingName",
133 attr
->spec
, ll_CC
)) != NULL
)
134 strlcpy(attr
->text
, locattr
->text
, sizeof(attr
->text
));
137 for (attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
139 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
141 cupsArraySave(ppd
->sorted_attrs
);
143 if ((locattr
= _ppdLocalizedAttr(ppd
, "cupsICCProfile", attr
->spec
,
145 strlcpy(attr
->text
, locattr
->text
, sizeof(attr
->text
));
147 cupsArrayRestore(ppd
->sorted_attrs
);
151 * Translate printer presets...
154 for (attr
= ppdFindAttr(ppd
, "APPrinterPreset", NULL
);
156 attr
= ppdFindNextAttr(ppd
, "APPrinterPreset", NULL
))
158 cupsArraySave(ppd
->sorted_attrs
);
160 if ((locattr
= _ppdLocalizedAttr(ppd
, "APPrinterPreset", attr
->spec
,
162 strlcpy(attr
->text
, locattr
->text
, sizeof(attr
->text
));
164 cupsArrayRestore(ppd
->sorted_attrs
);
172 * 'ppdLocalizeAttr()' - Localize an attribute.
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.
179 ppd_attr_t
* /* O - Localized attribute or @code NULL@ if none exists */
180 ppdLocalizeAttr(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 */
184 ppd_attr_t
*locattr
; /* Localized attribute */
185 char ll_CC
[6]; /* Language + country locale */
189 * Get the default language...
192 ppd_ll_CC(ll_CC
, sizeof(ll_CC
));
195 * Find the localized attribute...
199 locattr
= _ppdLocalizedAttr(ppd
, keyword
, spec
, ll_CC
);
201 locattr
= _ppdLocalizedAttr(ppd
, "Translation", keyword
, ll_CC
);
204 locattr
= ppdFindAttr(ppd
, keyword
, spec
);
211 * 'ppdLocalizeIPPReason()' - Get the localized version of a cupsIPPReason
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.
219 * If no value of the requested scheme can be found, NULL is returned.
221 * @since CUPS 1.3/macOS 10.5@
224 const char * /* O - Value or NULL if not found */
225 ppdLocalizeIPPReason(
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 */
232 cups_lang_t
*lang
; /* Current language */
233 ppd_attr_t
*locattr
; /* Localized attribute */
234 char ll_CC
[6], /* Language + country locale */
235 *bufptr
, /* Pointer into buffer */
236 *bufend
, /* Pointer to end of buffer */
237 *valptr
; /* Pointer into value */
238 int ch
; /* Hex-encoded character */
239 size_t schemelen
; /* Length of scheme name */
243 * Range check input...
249 if (!ppd
|| !reason
|| (scheme
&& !*scheme
) ||
250 !buffer
|| bufsize
< PPD_MAX_TEXT
)
254 * Get the default language...
257 lang
= ppd_ll_CC(ll_CC
, sizeof(ll_CC
));
260 * Find the localized attribute...
263 if ((locattr
= _ppdLocalizedAttr(ppd
, "cupsIPPReason", reason
,
265 locattr
= ppdFindAttr(ppd
, "cupsIPPReason", reason
);
269 if (lang
&& (!scheme
|| !strcmp(scheme
, "text")) && strcmp(reason
, "none"))
272 * Try to localize a standard printer-state-reason keyword...
275 char msgid
[1024], /* State message identifier */
276 *ptr
; /* Pointer to state suffix */
277 const char *message
= NULL
; /* Localized message */
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")))
283 message
= _cupsLangString(lang
, msgid
);
285 if (message
&& strcmp(message
, msgid
))
287 strlcpy(buffer
, _cupsLangString(lang
, message
), bufsize
);
296 * Now find the value we need...
299 bufend
= buffer
+ bufsize
- 1;
301 if (!scheme
|| !strcmp(scheme
, "text"))
304 * Copy a text value (either the translation text or text:... URIs from
308 strlcpy(buffer
, locattr
->text
, bufsize
);
310 for (valptr
= locattr
->value
, bufptr
= buffer
; *valptr
&& bufptr
< bufend
;)
312 if (!strncmp(valptr
, "text:", 5))
315 * Decode text: URI and add to the buffer...
320 while (*valptr
&& !_cups_isspace(*valptr
) && bufptr
< bufend
)
322 if (*valptr
== '%' && isxdigit(valptr
[1] & 255) &&
323 isxdigit(valptr
[2] & 255))
326 * Pull a hex-encoded character from the URI...
331 if (isdigit(*valptr
& 255))
332 ch
= (*valptr
- '0') << 4;
334 ch
= (tolower(*valptr
) - 'a' + 10) << 4;
337 if (isdigit(*valptr
& 255))
338 *bufptr
++ = (char)(ch
| (*valptr
- '0'));
340 *bufptr
++ = (char)(ch
| (tolower(*valptr
) - 'a' + 10));
343 else if (*valptr
== '+')
349 *bufptr
++ = *valptr
++;
358 while (*valptr
&& !_cups_isspace(*valptr
))
366 while (_cups_isspace(*valptr
))
381 schemelen
= strlen(scheme
);
382 if (scheme
[schemelen
- 1] == ':') /* Force scheme to be just the name */
385 for (valptr
= locattr
->value
, bufptr
= buffer
; *valptr
&& bufptr
< bufend
;)
387 if ((!strncmp(valptr
, scheme
, schemelen
) && valptr
[schemelen
] == ':') ||
388 (*valptr
== '/' && !strcmp(scheme
, "file")))
394 while (*valptr
&& !_cups_isspace(*valptr
) && bufptr
< bufend
)
395 *bufptr
++ = *valptr
++;
407 while (*valptr
&& !_cups_isspace(*valptr
))
415 while (_cups_isspace(*valptr
))
425 * 'ppdLocalizeMarkerName()' - Get the localized version of a marker-names
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.
432 * @since CUPS 1.4/macOS 10.6@
435 const char * /* O - Value or @code NULL@ if not found */
436 ppdLocalizeMarkerName(
437 ppd_file_t
*ppd
, /* I - PPD file */
438 const char *name
) /* I - Marker name to look up */
440 ppd_attr_t
*locattr
; /* Localized attribute */
441 char ll_CC
[6]; /* Language + country locale */
445 * Range check input...
452 * Get the default language...
455 ppd_ll_CC(ll_CC
, sizeof(ll_CC
));
458 * Find the localized attribute...
461 if ((locattr
= _ppdLocalizedAttr(ppd
, "cupsMarkerName", name
,
463 locattr
= ppdFindAttr(ppd
, "cupsMarkerName", name
);
465 return (locattr
? locattr
->text
: NULL
);
470 * '_ppdFreeLanguages()' - Free an array of languages from _ppdGetLanguages.
475 cups_array_t
*languages
) /* I - Languages array */
477 char *language
; /* Current language */
480 for (language
= (char *)cupsArrayFirst(languages
);
482 language
= (char *)cupsArrayNext(languages
))
485 cupsArrayDelete(languages
);
490 * '_ppdGetLanguages()' - Get an array of languages from a PPD file.
493 cups_array_t
* /* O - Languages array */
494 _ppdGetLanguages(ppd_file_t
*ppd
) /* I - PPD file */
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 */
504 * See if we have a cupsLanguages attribute...
507 if ((attr
= ppdFindAttr(ppd
, "cupsLanguages", NULL
)) == NULL
|| !attr
->value
)
511 * Yes, load the list...
514 if ((languages
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
)) == NULL
)
517 if ((value
= strdup(attr
->value
)) == NULL
)
519 cupsArrayDelete(languages
);
523 for (ptr
= value
; *ptr
;)
526 * Skip leading whitespace...
529 while (_cups_isspace(*ptr
))
536 * Find the end of this language name...
539 for (start
= ptr
; *ptr
&& !_cups_isspace(*ptr
); ptr
++);
544 if (!strcmp(start
, "en"))
547 cupsArrayAdd(languages
, strdup(start
));
551 * Free the temporary string and return either an array with one or more
552 * values or a NULL pointer...
557 if (cupsArrayCount(languages
) == 0)
559 cupsArrayDelete(languages
);
568 * '_ppdHashName()' - Generate a hash value for a device or profile name.
570 * This function is primarily used on macOS, but is generally accessible
571 * since cupstestppd needs to check for profile name collisions in PPD files...
574 unsigned /* O - Hash value */
575 _ppdHashName(const char *name
) /* I - Name to hash */
577 unsigned mult
, /* Multiplier */
578 hash
= 0; /* Hash value */
581 for (mult
= 1; *name
&& mult
<= 128; mult
++, name
++)
582 hash
+= (*name
& 255) * mult
;
589 * '_ppdLocalizedAttr()' - Find a localized attribute.
592 ppd_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 */
598 char lkeyword
[PPD_MAX_NAME
]; /* Localization keyword */
599 ppd_attr_t
*attr
; /* Current attribute */
602 DEBUG_printf(("4_ppdLocalizedAttr(ppd=%p, keyword=\"%s\", spec=\"%s\", "
603 "ll_CC=\"%s\")", ppd
, keyword
, spec
, ll_CC
));
606 * Look for Keyword.ll_CC, then Keyword.ll...
609 snprintf(lkeyword
, sizeof(lkeyword
), "%s.%s", ll_CC
, keyword
);
610 if ((attr
= ppdFindAttr(ppd
, lkeyword
, spec
)) == NULL
)
613 * <rdar://problem/22130168>
615 * Multiple locales need special handling... Sigh...
618 if (!strcmp(ll_CC
, "zh_HK"))
620 snprintf(lkeyword
, sizeof(lkeyword
), "zh_TW.%s", keyword
);
621 attr
= ppdFindAttr(ppd
, lkeyword
, spec
);
626 snprintf(lkeyword
, sizeof(lkeyword
), "%2.2s.%s", ll_CC
, keyword
);
627 attr
= ppdFindAttr(ppd
, lkeyword
, spec
);
632 if (!strncmp(ll_CC
, "ja", 2))
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...
641 snprintf(lkeyword
, sizeof(lkeyword
), "jp.%s", keyword
);
642 attr
= ppdFindAttr(ppd
, lkeyword
, spec
);
644 else if (!strncmp(ll_CC
, "nb", 2))
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"...
652 snprintf(lkeyword
, sizeof(lkeyword
), "no.%s", keyword
);
653 attr
= ppdFindAttr(ppd
, lkeyword
, spec
);
655 else if (!strncmp(ll_CC
, "no", 2))
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...
663 snprintf(lkeyword
, sizeof(lkeyword
), "nb.%s", keyword
);
664 attr
= ppdFindAttr(ppd
, lkeyword
, spec
);
671 DEBUG_printf(("5_ppdLocalizedAttr: *%s %s/%s: \"%s\"\n", attr
->name
,
672 attr
->spec
, attr
->text
, attr
->value
? attr
->value
: ""));
674 DEBUG_puts("5_ppdLocalizedAttr: NOT FOUND");
682 * 'ppd_ll_CC()' - Get the current locale names.
685 static cups_lang_t
* /* O - Current language */
686 ppd_ll_CC(char *ll_CC
, /* O - Country-specific locale name */
687 size_t ll_CC_size
) /* I - Size of country-specific name */
689 cups_lang_t
*lang
; /* Current language */
693 * Get the current locale...
696 if ((lang
= cupsLangDefault()) == NULL
)
698 strlcpy(ll_CC
, "en_US", ll_CC_size
);
703 * Copy the locale name...
706 strlcpy(ll_CC
, lang
->language
, ll_CC_size
);
708 if (strlen(ll_CC
) == 2)
711 * Map "ll" to primary/origin country locales to have the best
712 * chance of finding a match...
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
);
727 DEBUG_printf(("8ppd_ll_CC: lang->language=\"%s\", ll_CC=\"%s\"...",
728 lang
->language
, ll_CC
));