]>
git.ipfire.org Git - thirdparty/cups.git/blob - cups/localize.c
2 * "$Id: localize.c 7679 2008-06-19 23:37:45Z mike $"
4 * PPD localization routines for the Common UNIX Printing System (CUPS).
6 * Copyright 2007-2009 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
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/".
15 * PostScript is a trademark of Adobe Systems, Inc.
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
24 * This file is subject to the Apple OS-Developed Software exception.
28 * ppdLocalize() - Localize the PPD file to the current locale.
29 * ppdLocalizeAttr() - Localize an attribute.
30 * ppdLocalizeIPPReason() - Get the localized version of a cupsIPPReason
32 * ppdLocalizeMarkerName() - Get the localized version of a marker-names
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
38 * _ppdLocalizedAttr() - Find a localized attribute.
39 * ppd_ll_CC() - Get the current locale names.
43 * Include necessary headers.
47 #include "ppd-private.h"
55 static cups_lang_t
*ppd_ll_CC(char *ll_CC
, int ll_CC_size
);
59 * 'ppdLocalize()' - Localize the PPD file to the current locale.
61 * All groups, options, and choices are localized, as are ICC profile
62 * descriptions, printer presets, and custom option parameters. Each
63 * localized string uses the UTF-8 character encoding.
65 * @since CUPS 1.2/Mac OS X 10.5@
68 int /* O - 0 on success, -1 on error */
69 ppdLocalize(ppd_file_t
*ppd
) /* I - PPD file */
71 int i
, j
, k
; /* Looping vars */
72 ppd_group_t
*group
; /* Current group */
73 ppd_option_t
*option
; /* Current option */
74 ppd_choice_t
*choice
; /* Current choice */
75 ppd_coption_t
*coption
; /* Current custom option */
76 ppd_cparam_t
*cparam
; /* Current custom parameter */
77 ppd_attr_t
*attr
, /* Current attribute */
78 *locattr
; /* Localized attribute */
79 char ckeyword
[PPD_MAX_NAME
], /* Custom keyword */
80 ll_CC
[6]; /* Language + country locale */
84 * Range check input...
87 DEBUG_printf(("ppdLocalize(ppd=%p)", ppd
));
93 * Get the default language...
96 ppd_ll_CC(ll_CC
, sizeof(ll_CC
));
99 * Now lookup all of the groups, options, choices, etc.
102 for (i
= ppd
->num_groups
, group
= ppd
->groups
; i
> 0; i
--, group
++)
104 if ((locattr
= _ppdLocalizedAttr(ppd
, "Translation", group
->name
,
106 strlcpy(group
->text
, locattr
->text
, sizeof(group
->text
));
108 for (j
= group
->num_options
, option
= group
->options
; j
> 0; j
--, option
++)
110 if ((locattr
= _ppdLocalizedAttr(ppd
, "Translation", option
->keyword
,
112 strlcpy(option
->text
, locattr
->text
, sizeof(option
->text
));
114 for (k
= option
->num_choices
, choice
= option
->choices
;
118 if (strcmp(choice
->choice
, "Custom") ||
119 !ppdFindCustomOption(ppd
, option
->keyword
))
120 locattr
= _ppdLocalizedAttr(ppd
, option
->keyword
, choice
->choice
,
124 snprintf(ckeyword
, sizeof(ckeyword
), "Custom%s", option
->keyword
);
126 locattr
= _ppdLocalizedAttr(ppd
, ckeyword
, "True", ll_CC
);
130 strlcpy(choice
->text
, locattr
->text
, sizeof(choice
->text
));
136 * Translate any custom parameters...
139 for (coption
= (ppd_coption_t
*)cupsArrayFirst(ppd
->coptions
);
141 coption
= (ppd_coption_t
*)cupsArrayNext(ppd
->coptions
))
143 for (cparam
= (ppd_cparam_t
*)cupsArrayFirst(coption
->params
);
145 cparam
= (ppd_cparam_t
*)cupsArrayNext(coption
->params
))
147 snprintf(ckeyword
, sizeof(ckeyword
), "ParamCustom%s", coption
->keyword
);
149 if ((locattr
= _ppdLocalizedAttr(ppd
, ckeyword
, cparam
->name
,
151 strlcpy(cparam
->text
, locattr
->text
, sizeof(cparam
->text
));
156 * Translate ICC profile names...
159 if ((attr
= ppdFindAttr(ppd
, "APCustomColorMatchingName", NULL
)) != NULL
)
161 if ((locattr
= _ppdLocalizedAttr(ppd
, "APCustomColorMatchingName",
162 attr
->spec
, ll_CC
)) != NULL
)
163 strlcpy(attr
->text
, locattr
->text
, sizeof(attr
->text
));
166 for (attr
= ppdFindAttr(ppd
, "cupsICCProfile", NULL
);
168 attr
= ppdFindNextAttr(ppd
, "cupsICCProfile", NULL
))
170 cupsArraySave(ppd
->sorted_attrs
);
172 if ((locattr
= _ppdLocalizedAttr(ppd
, "cupsICCProfile", attr
->spec
,
174 strlcpy(attr
->text
, locattr
->text
, sizeof(attr
->text
));
176 cupsArrayRestore(ppd
->sorted_attrs
);
180 * Translate printer presets...
183 for (attr
= ppdFindAttr(ppd
, "APPrinterPreset", NULL
);
185 attr
= ppdFindNextAttr(ppd
, "APPrinterPreset", NULL
))
187 cupsArraySave(ppd
->sorted_attrs
);
189 if ((locattr
= _ppdLocalizedAttr(ppd
, "APPrinterPreset", attr
->spec
,
191 strlcpy(attr
->text
, locattr
->text
, sizeof(attr
->text
));
193 cupsArrayRestore(ppd
->sorted_attrs
);
201 * 'ppdLocalizeAttr()' - Localize an attribute.
203 * This function uses the current locale to find the localized attribute for
204 * the given main and option keywords. If no localized version of the
205 * attribute exists for the current locale, the unlocalized version is returned.
208 ppd_attr_t
* /* O - Localized attribute or @code NULL@ if none exists */
209 ppdLocalizeAttr(ppd_file_t
*ppd
, /* I - PPD file */
210 const char *keyword
, /* I - Main keyword */
211 const char *spec
) /* I - Option keyword or @code NULL@ for none */
213 ppd_attr_t
*locattr
; /* Localized attribute */
214 char ll_CC
[6]; /* Language + country locale */
218 * Get the default language...
221 ppd_ll_CC(ll_CC
, sizeof(ll_CC
));
224 * Find the localized attribute...
228 locattr
= _ppdLocalizedAttr(ppd
, keyword
, spec
, ll_CC
);
230 locattr
= _ppdLocalizedAttr(ppd
, "Translation", keyword
, ll_CC
);
233 locattr
= ppdFindAttr(ppd
, keyword
, spec
);
240 * 'ppdLocalizeIPPReason()' - Get the localized version of a cupsIPPReason
243 * This function uses the current locale to find the corresponding reason
244 * text or URI from the attribute value. If "scheme" is NULL or "text",
245 * the returned value contains human-readable (UTF-8) text from the translation
246 * string or attribute value. Otherwise the corresponding URI is returned.
248 * If no value of the requested scheme can be found, NULL is returned.
250 * @since CUPS 1.3/Mac OS X 10.5@
253 const char * /* O - Value or NULL if not found */
254 ppdLocalizeIPPReason(
255 ppd_file_t
*ppd
, /* I - PPD file */
256 const char *reason
, /* I - IPP reason keyword to look up */
257 const char *scheme
, /* I - URI scheme or NULL for text */
258 char *buffer
, /* I - Value buffer */
259 size_t bufsize
) /* I - Size of value buffer */
261 cups_lang_t
*lang
; /* Current language */
262 ppd_attr_t
*locattr
; /* Localized attribute */
263 char ll_CC
[6], /* Language + country locale */
264 *bufptr
, /* Pointer into buffer */
265 *bufend
, /* Pointer to end of buffer */
266 *valptr
; /* Pointer into value */
267 int ch
, /* Hex-encoded character */
268 schemelen
; /* Length of scheme name */
272 * Range check input...
278 if (!ppd
|| !reason
|| (scheme
&& !*scheme
) ||
279 !buffer
|| bufsize
< PPD_MAX_TEXT
)
283 * Get the default language...
286 lang
= ppd_ll_CC(ll_CC
, sizeof(ll_CC
));
289 * Find the localized attribute...
292 if ((locattr
= _ppdLocalizedAttr(ppd
, "cupsIPPReason", reason
,
294 locattr
= ppdFindAttr(ppd
, "cupsIPPReason", reason
);
298 if (lang
&& (!scheme
|| !strcmp(scheme
, "text")))
301 * Try to localize a standard printer-state-reason keyword...
304 const char *message
= NULL
; /* Localized message */
307 if (!strncmp(reason
, "media-needed", 12))
308 message
= _("Media tray needs to be filled.");
309 else if (!strncmp(reason
, "media-jam", 9))
310 message
= _("Media jam!");
311 else if (!strncmp(reason
, "moving-to-paused", 16) ||
312 !strncmp(reason
, "offline", 7) ||
313 !strncmp(reason
, "paused", 6) ||
314 !strncmp(reason
, "shutdown", 8))
315 message
= _("Printer offline.");
316 else if (!strncmp(reason
, "toner-low", 9))
317 message
= _("Toner low.");
318 else if (!strncmp(reason
, "toner-empty", 11))
319 message
= _("Out of toner!");
320 else if (!strncmp(reason
, "cover-open", 10))
321 message
= _("Cover open.");
322 else if (!strncmp(reason
, "interlock-open", 14))
323 message
= _("Interlock open.");
324 else if (!strncmp(reason
, "door-open", 9))
325 message
= _("Door open.");
326 else if (!strncmp(reason
, "input-tray-missing", 18))
327 message
= _("Media tray missing!");
328 else if (!strncmp(reason
, "media-low", 9))
329 message
= _("Media tray almost empty.");
330 else if (!strncmp(reason
, "media-empty", 11))
331 message
= _("Media tray empty!");
332 else if (!strncmp(reason
, "output-tray-missing", 19))
333 message
= _("Output tray missing!");
334 else if (!strncmp(reason
, "output-area-almost-full", 23))
335 message
= _("Output bin almost full.");
336 else if (!strncmp(reason
, "output-area-full", 16))
337 message
= _("Output bin full!");
338 else if (!strncmp(reason
, "marker-supply-low", 17))
339 message
= _("Ink/toner almost empty.");
340 else if (!strncmp(reason
, "marker-supply-empty", 19))
341 message
= _("Ink/toner empty!");
342 else if (!strncmp(reason
, "marker-waste-almost-full", 24))
343 message
= _("Ink/toner waste bin almost full.");
344 else if (!strncmp(reason
, "marker-waste-full", 17))
345 message
= _("Ink/toner waste bin full!");
346 else if (!strncmp(reason
, "fuser-over-temp", 15))
347 message
= _("Fuser temperature high!");
348 else if (!strncmp(reason
, "fuser-under-temp", 16))
349 message
= _("Fuser temperature low!");
350 else if (!strncmp(reason
, "opc-near-eol", 12))
351 message
= _("OPC almost at end-of-life.");
352 else if (!strncmp(reason
, "opc-life-over", 13))
353 message
= _("OPC at end-of-life!");
354 else if (!strncmp(reason
, "developer-low", 13))
355 message
= _("Developer almost empty.");
356 else if (!strncmp(reason
, "developer-empty", 15))
357 message
= _("Developer empty!");
361 strlcpy(buffer
, _cupsLangString(lang
, message
), bufsize
);
370 * Now find the value we need...
373 bufend
= buffer
+ bufsize
- 1;
375 if (!scheme
|| !strcmp(scheme
, "text"))
378 * Copy a text value (either the translation text or text:... URIs from
382 strlcpy(buffer
, locattr
->text
, bufsize
);
384 for (valptr
= locattr
->value
, bufptr
= buffer
; *valptr
&& bufptr
< bufend
;)
386 if (!strncmp(valptr
, "text:", 5))
389 * Decode text: URI and add to the buffer...
394 while (*valptr
&& !isspace(*valptr
& 255) && bufptr
< bufend
)
396 if (*valptr
== '%' && isxdigit(valptr
[1] & 255) &&
397 isxdigit(valptr
[2] & 255))
400 * Pull a hex-encoded character from the URI...
405 if (isdigit(*valptr
& 255))
406 ch
= (*valptr
- '0') << 4;
408 ch
= (tolower(*valptr
) - 'a' + 10) << 4;
411 if (isdigit(*valptr
& 255))
412 *bufptr
++ = ch
| (*valptr
- '0');
414 *bufptr
++ = ch
| (tolower(*valptr
) - 'a' + 10);
417 else if (*valptr
== '+')
423 *bufptr
++ = *valptr
++;
432 while (*valptr
&& !isspace(*valptr
& 255))
440 while (isspace(*valptr
& 255))
455 schemelen
= strlen(scheme
);
456 if (scheme
[schemelen
- 1] == ':') /* Force scheme to be just the name */
459 for (valptr
= locattr
->value
, bufptr
= buffer
; *valptr
&& bufptr
< bufend
;)
461 if ((!strncmp(valptr
, scheme
, schemelen
) && valptr
[schemelen
] == ':') ||
462 (*valptr
== '/' && !strcmp(scheme
, "file")))
468 while (*valptr
&& !isspace(*valptr
& 255) && bufptr
< bufend
)
469 *bufptr
++ = *valptr
++;
481 while (*valptr
&& !isspace(*valptr
& 255))
489 while (isspace(*valptr
& 255))
499 * 'ppdLocalizeMarkerName()' - Get the localized version of a marker-names
502 * This function uses the current locale to find the corresponding name
503 * text from the attribute value. If no localized text for the requested
504 * name can be found, @code NULL@ is returned.
509 const char * /* O - Value or @code NULL@ if not found */
510 ppdLocalizeMarkerName(
511 ppd_file_t
*ppd
, /* I - PPD file */
512 const char *name
) /* I - Marker name to look up */
514 ppd_attr_t
*locattr
; /* Localized attribute */
515 char ll_CC
[6]; /* Language + country locale */
519 * Range check input...
526 * Get the default language...
529 ppd_ll_CC(ll_CC
, sizeof(ll_CC
));
532 * Find the localized attribute...
535 if ((locattr
= _ppdLocalizedAttr(ppd
, "cupsMarkerName", name
,
537 locattr
= ppdFindAttr(ppd
, "cupsMarkerName", name
);
539 return (locattr
? locattr
->text
: NULL
);
544 * '_ppdFreeLanguages()' - Free an array of languages from _ppdGetLanguages.
549 cups_array_t
*languages
) /* I - Languages array */
551 char *language
; /* Current language */
554 for (language
= (char *)cupsArrayFirst(languages
);
556 language
= (char *)cupsArrayNext(languages
))
559 cupsArrayDelete(languages
);
564 * '_ppdGetLanguages()' - Get an array of languages from a PPD file.
567 cups_array_t
* /* O - Languages array */
568 _ppdGetLanguages(ppd_file_t
*ppd
) /* I - PPD file */
570 cups_array_t
*languages
; /* Languages array */
571 ppd_attr_t
*attr
; /* cupsLanguages attribute */
572 char *value
, /* Copy of attribute value */
573 *start
, /* Start of current language */
574 *ptr
; /* Pointer into languages */
578 * See if we have a cupsLanguages attribute...
581 if ((attr
= ppdFindAttr(ppd
, "cupsLanguages", NULL
)) == NULL
|| !attr
->value
)
585 * Yes, load the list...
588 if ((languages
= cupsArrayNew((cups_array_func_t
)strcmp
, NULL
)) == NULL
)
591 if ((value
= strdup(attr
->value
)) == NULL
)
593 cupsArrayDelete(languages
);
597 for (ptr
= value
; *ptr
;)
600 * Skip leading whitespace...
603 while (isspace(*ptr
& 255))
610 * Find the end of this language name...
613 for (start
= ptr
; *ptr
&& !isspace(*ptr
& 255); ptr
++);
618 if (!strcmp(start
, "en"))
621 cupsArrayAdd(languages
, strdup(start
));
625 * Free the temporary string and return either an array with one or more
626 * values or a NULL pointer...
631 if (cupsArrayCount(languages
) == 0)
633 cupsArrayDelete(languages
);
642 * '_ppdHashName()' - Generate a hash value for a device or profile name.
644 * This function is primarily used on Mac OS X, but is generally accessible
645 * since cupstestppd needs to check for profile name collisions in PPD files...
648 unsigned /* O - Hash value */
649 _ppdHashName(const char *name
) /* I - Name to hash */
651 int mult
; /* Multiplier */
652 unsigned hash
= 0; /* Hash value */
655 for (mult
= 1; *name
&& mult
<= 128; mult
++, name
++)
656 hash
+= (*name
& 255) * mult
;
663 * '_ppdLocalizedAttr()' - Find a localized attribute.
666 ppd_attr_t
* /* O - Localized attribute or NULL */
667 _ppdLocalizedAttr(ppd_file_t
*ppd
, /* I - PPD file */
668 const char *keyword
, /* I - Main keyword */
669 const char *spec
, /* I - Option keyword */
670 const char *ll_CC
) /* I - Language + country locale */
672 char lkeyword
[PPD_MAX_NAME
]; /* Localization keyword */
673 ppd_attr_t
*attr
; /* Current attribute */
676 DEBUG_printf(("4_ppdLocalizedAttr(ppd=%p, keyword=\"%s\", spec=\"%s\", "
677 "ll_CC=\"%s\")", ppd
, keyword
, spec
, ll_CC
));
680 * Look for Keyword.ll_CC, then Keyword.ll...
683 snprintf(lkeyword
, sizeof(lkeyword
), "%s.%s", ll_CC
, keyword
);
684 if ((attr
= ppdFindAttr(ppd
, lkeyword
, spec
)) == NULL
)
686 snprintf(lkeyword
, sizeof(lkeyword
), "%2.2s.%s", ll_CC
, keyword
);
687 attr
= ppdFindAttr(ppd
, lkeyword
, spec
);
691 if (!strncmp(ll_CC
, "ja", 2))
694 * Due to a bug in the CUPS DDK 1.1.0 ppdmerge program, Japanese
695 * PPD files were incorrectly assigned "jp" as the locale name
696 * instead of "ja". Support both the old (incorrect) and new
697 * locale names for Japanese...
700 snprintf(lkeyword
, sizeof(lkeyword
), "jp.%s", keyword
);
701 attr
= ppdFindAttr(ppd
, lkeyword
, spec
);
703 else if (!strncmp(ll_CC
, "no", 2))
706 * Norway has two languages, "Bokmal" (the primary one)
707 * and "Nynorsk" (new Norwegian); we map "no" to "nb" here as
708 * recommended by the locale folks...
711 snprintf(lkeyword
, sizeof(lkeyword
), "nb.%s", keyword
);
712 attr
= ppdFindAttr(ppd
, lkeyword
, spec
);
719 DEBUG_printf(("5_ppdLocalizedAttr: *%s %s/%s: \"%s\"\n", attr
->name
,
720 attr
->spec
, attr
->text
, attr
->value
? attr
->value
: ""));
722 DEBUG_puts("5_ppdLocalizedAttr: NOT FOUND");
730 * 'ppd_ll_CC()' - Get the current locale names.
733 static cups_lang_t
* /* O - Current language */
734 ppd_ll_CC(char *ll_CC
, /* O - Country-specific locale name */
735 int ll_CC_size
) /* I - Size of country-specific name */
737 cups_lang_t
*lang
; /* Current language */
741 * Get the current locale...
744 if ((lang
= cupsLangDefault()) == NULL
)
746 strlcpy(ll_CC
, "en_US", ll_CC_size
);
751 * Copy the locale name...
754 strlcpy(ll_CC
, lang
->language
, ll_CC_size
);
756 if (strlen(ll_CC
) == 2)
759 * Map "ll" to primary/origin country locales to have the best
760 * chance of finding a match...
763 if (!strcmp(ll_CC
, "cs"))
764 strlcpy(ll_CC
, "cs_CZ", ll_CC_size
);
765 else if (!strcmp(ll_CC
, "en"))
766 strlcpy(ll_CC
, "en_US", ll_CC_size
);
767 else if (!strcmp(ll_CC
, "ja"))
768 strlcpy(ll_CC
, "ja_JP", ll_CC_size
);
769 else if (!strcmp(ll_CC
, "sv"))
770 strlcpy(ll_CC
, "sv_SE", ll_CC_size
);
771 else if (!strcmp(ll_CC
, "zh")) /* Simplified Chinese */
772 strlcpy(ll_CC
, "zh_CN", ll_CC_size
);
775 DEBUG_printf(("8ppd_ll_CC: lang->language=\"%s\", ll_CC=\"%s\"...",
776 lang
->language
, ll_CC
));
782 * End of "$Id: localize.c 7679 2008-06-19 23:37:45Z mike $".