]> git.ipfire.org Git - thirdparty/cups.git/blob - cups/localize.c
Merge changes from CUPS 1.4svn-r7282.
[thirdparty/cups.git] / cups / localize.c
1 /*
2 * "$Id: localize.c 6882 2007-08-29 21:05:10Z mike $"
3 *
4 * PPD localization routines for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007-2008 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 * ppdLocalizeIPPReason() - Get the localized version of a cupsIPPReason
30 * attribute.
31 * ppdLocalizeMarkerName() - Get the localized version of a marker-names
32 * attribute value.
33 * ppd_ll_CC() - Get the current locale names.
34 * ppd_localized_attr() - Find a localized attribute.
35 */
36
37 /*
38 * Include necessary headers.
39 */
40
41 #include "globals.h"
42 #include "debug.h"
43
44
45 /*
46 * Local functions...
47 */
48
49 static void ppd_ll_CC(char *ll_CC, int ll_CC_size,
50 char *ll, int ll_size);
51 static ppd_attr_t *ppd_localized_attr(ppd_file_t *ppd,
52 const char *keyword,
53 const char *spec, const char *ll_CC,
54 const char *ll);
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@
65 */
66
67 int /* O - 0 on success, -1 on error */
68 ppdLocalize(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 ll[3]; /* Language locale */
81
82
83 /*
84 * Range check input...
85 */
86
87 DEBUG_printf(("ppdLocalize(ppd=%p)\n", ppd));
88
89 if (!ppd)
90 return (-1);
91
92 /*
93 * Get the default language...
94 */
95
96 ppd_ll_CC(ll_CC, sizeof(ll_CC), ll, sizeof(ll));
97
98 /*
99 * Now lookup all of the groups, options, choices, etc.
100 */
101
102 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
103 {
104 if ((locattr = ppd_localized_attr(ppd, "Translation", group->name,
105 ll_CC, ll)) != NULL)
106 strlcpy(group->text, locattr->text, sizeof(group->text));
107
108 for (j = group->num_options, option = group->options; j > 0; j --, option ++)
109 {
110 if ((locattr = ppd_localized_attr(ppd, "Translation", option->keyword,
111 ll_CC, ll)) != NULL)
112 strlcpy(option->text, locattr->text, sizeof(option->text));
113
114 for (k = option->num_choices, choice = option->choices;
115 k > 0;
116 k --, choice ++)
117 {
118 if (strcmp(choice->choice, "Custom"))
119 locattr = ppd_localized_attr(ppd, option->keyword, choice->choice,
120 ll_CC, ll);
121 else
122 {
123 snprintf(ckeyword, sizeof(ckeyword), "Custom%s", option->keyword);
124
125 locattr = ppd_localized_attr(ppd, ckeyword, "True", ll_CC, ll);
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 = ppd_localized_attr(ppd, ckeyword, cparam->name,
149 ll_CC, ll)) != 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 = ppd_localized_attr(ppd, "APCustomColorMatchingName",
161 attr->spec, ll_CC, ll)) != 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 = ppd_localized_attr(ppd, "cupsICCProfile", attr->spec,
172 ll_CC, ll)) != 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 = ppd_localized_attr(ppd, "APPrinterPreset", attr->spec,
189 ll_CC, ll)) != 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 * 'ppdLocalizeIPPReason()' - Get the localized version of a cupsIPPReason
201 * attribute.
202 *
203 * This function uses the current locale to find the corresponding reason
204 * text or URI from the attribute value. If "scheme" is NULL or "text",
205 * the returned value contains human-readable (UTF-8) text from the translation
206 * string or attribute value. Otherwise the corresponding URI is returned.
207 *
208 * If no value of the requested scheme can be found, NULL is returned.
209 *
210 * @since CUPS 1.3@
211 */
212
213 const char * /* O - Value or NULL if not found */
214 ppdLocalizeIPPReason(
215 ppd_file_t *ppd, /* I - PPD file */
216 const char *reason, /* I - IPP reason keyword to look up */
217 const char *scheme, /* I - URI scheme or NULL for text */
218 char *buffer, /* I - Value buffer */
219 size_t bufsize) /* I - Size of value buffer */
220 {
221 ppd_attr_t *locattr; /* Localized attribute */
222 char ll_CC[6], /* Language + country locale */
223 ll[3], /* Language locale */
224 *bufptr, /* Pointer into buffer */
225 *bufend, /* Pointer to end of buffer */
226 *valptr; /* Pointer into value */
227 int ch, /* Hex-encoded character */
228 schemelen; /* Length of scheme name */
229
230
231 /*
232 * Range check input...
233 */
234
235 if (buffer)
236 *buffer = '\0';
237
238 if (!ppd || !reason || (scheme && !*scheme) ||
239 !buffer || bufsize < PPD_MAX_TEXT)
240 return (NULL);
241
242 /*
243 * Get the default language...
244 */
245
246 ppd_ll_CC(ll_CC, sizeof(ll_CC), ll, sizeof(ll));
247
248 /*
249 * Find the localized attribute...
250 */
251
252 if ((locattr = ppd_localized_attr(ppd, "cupsIPPReason", reason,
253 ll_CC, ll)) == NULL)
254 locattr = ppdFindAttr(ppd, "cupsIPPReason", reason);
255
256 if (!locattr)
257 return (NULL);
258
259 /*
260 * Now find the value we need...
261 */
262
263 bufend = buffer + bufsize - 1;
264
265 if (!scheme || !strcmp(scheme, "text"))
266 {
267 /*
268 * Copy a text value (either the translation text or text:... URIs from
269 * the value...
270 */
271
272 strlcpy(buffer, locattr->text, bufsize);
273
274 for (valptr = locattr->value, bufptr = buffer; *valptr && bufptr < bufend;)
275 {
276 if (!strncmp(valptr, "text:", 5))
277 {
278 /*
279 * Decode text: URI and add to the buffer...
280 */
281
282 if (bufptr > buffer)
283 *bufptr++ = ' '; /* Add leading whitespace */
284
285 valptr += 5;
286
287 while (*valptr && !isspace(*valptr & 255) && bufptr < bufend)
288 {
289 if (*valptr == '%' && isxdigit(valptr[1] & 255) &&
290 isxdigit(valptr[2] & 255))
291 {
292 /*
293 * Pull a hex-encoded character from the URI...
294 */
295
296 valptr ++;
297
298 if (isdigit(*valptr & 255))
299 ch = (*valptr - '0') << 4;
300 else
301 ch = (tolower(*valptr) - 'a' + 10) << 4;
302 valptr ++;
303
304 if (isdigit(*valptr & 255))
305 *bufptr++ = ch | (*valptr - '0');
306 else
307 *bufptr++ = ch | (tolower(*valptr) - 'a' + 10);
308 valptr ++;
309 }
310 else if (*valptr == '+')
311 {
312 *bufptr++ = ' ';
313 valptr ++;
314 }
315 else
316 *bufptr++ = *valptr++;
317 }
318 }
319 else
320 {
321 /*
322 * Skip this URI...
323 */
324
325 while (*valptr && !isspace(*valptr & 255))
326 valptr++;
327 }
328
329 /*
330 * Skip whitespace...
331 */
332
333 while (isspace(*valptr & 255))
334 valptr ++;
335 }
336
337 if (bufptr > buffer)
338 *bufptr = '\0';
339
340 return (buffer);
341 }
342 else
343 {
344 /*
345 * Copy a URI...
346 */
347
348 schemelen = strlen(scheme);
349 if (scheme[schemelen - 1] == ':') /* Force scheme to be just the name */
350 schemelen --;
351
352 for (valptr = locattr->value, bufptr = buffer; *valptr && bufptr < bufend;)
353 {
354 if ((!strncmp(valptr, scheme, schemelen) && valptr[schemelen] == ':') ||
355 (*valptr == '/' && !strcmp(scheme, "file")))
356 {
357 /*
358 * Copy URI...
359 */
360
361 while (*valptr && !isspace(*valptr & 255) && bufptr < bufend)
362 *bufptr++ = *valptr++;
363
364 *bufptr = '\0';
365
366 return (buffer);
367 }
368 else
369 {
370 /*
371 * Skip this URI...
372 */
373
374 while (*valptr && !isspace(*valptr & 255))
375 valptr++;
376 }
377
378 /*
379 * Skip whitespace...
380 */
381
382 while (isspace(*valptr & 255))
383 valptr ++;
384 }
385
386 return (NULL);
387 }
388 }
389
390
391 /*
392 * 'ppdLocalizeMarkerName()' - Get the localized version of a marker-names
393 * attribute value.
394 *
395 * This function uses the current locale to find the corresponding name
396 * text from the attribute value. If no localized text for the requested
397 * name can be found, @code NULL@ is returned.
398 *
399 * @since CUPS 1.4@
400 */
401
402 const char * /* O - Value or @code NULL@ if not found */
403 ppdLocalizeMarkerName(
404 ppd_file_t *ppd, /* I - PPD file */
405 const char *name) /* I - Marker name to look up */
406 {
407 ppd_attr_t *locattr; /* Localized attribute */
408 char ll_CC[6], /* Language + country locale */
409 ll[3]; /* Language locale */
410
411
412 /*
413 * Range check input...
414 */
415
416 if (!ppd || !name)
417 return (NULL);
418
419 /*
420 * Get the default language...
421 */
422
423 ppd_ll_CC(ll_CC, sizeof(ll_CC), ll, sizeof(ll));
424
425 /*
426 * Find the localized attribute...
427 */
428
429 if ((locattr = ppd_localized_attr(ppd, "cupsMarkerName", name,
430 ll_CC, ll)) == NULL)
431 locattr = ppdFindAttr(ppd, "cupsMarkerName", name);
432
433 return (locattr ? locattr->text : NULL);
434 }
435
436
437 /*
438 * 'ppd_ll_CC()' - Get the current locale names.
439 */
440
441 static void
442 ppd_ll_CC(char *ll_CC, /* O - Country-specific locale name */
443 int ll_CC_size, /* I - Size of country-specific name */
444 char *ll, /* O - Generic locale name */
445 int ll_size) /* I - Size of generic name */
446 {
447 cups_lang_t *lang; /* Current language */
448
449
450 /*
451 * Get the current locale...
452 */
453
454 if ((lang = cupsLangDefault()) == NULL)
455 {
456 strlcpy(ll_CC, "en_US", ll_CC_size);
457 strlcpy(ll, "en", ll_size);
458 return;
459 }
460
461 /*
462 * Copy the locale name...
463 */
464
465 strlcpy(ll_CC, lang->language, ll_CC_size);
466 strlcpy(ll, lang->language, ll_size);
467
468 DEBUG_printf(("ll_CC=\"%s\", ll=\"%s\"\n", ll_CC, ll));
469
470 if (strlen(ll_CC) == 2)
471 {
472 /*
473 * Map "ll" to primary/origin country locales to have the best
474 * chance of finding a match...
475 */
476
477 if (!strcmp(ll_CC, "cs"))
478 strlcpy(ll_CC, "cs_CZ", ll_CC_size);
479 else if (!strcmp(ll_CC, "en"))
480 strlcpy(ll_CC, "en_US", ll_CC_size);
481 else if (!strcmp(ll_CC, "ja"))
482 strlcpy(ll_CC, "ja_JP", ll_CC_size);
483 else if (!strcmp(ll_CC, "sv"))
484 strlcpy(ll_CC, "sv_SE", ll_CC_size);
485 else if (!strcmp(ll_CC, "zh")) /* Simplified Chinese */
486 strlcpy(ll_CC, "zh_CN", ll_CC_size);
487 else if (ll_CC_size >= 6)
488 {
489 ll_CC[2] = '_';
490 ll_CC[3] = toupper(ll_CC[0] & 255);
491 ll_CC[4] = toupper(ll_CC[1] & 255);
492 ll_CC[5] = '\0';
493 }
494 }
495
496 DEBUG_printf(("ppd_ll_CC: lang->language=\"%s\", ll=\"%s\", ll_CC=\"%s\"...\n",
497 lang->language, ll, ll_CC));
498 }
499
500
501 /*
502 * 'ppd_localized_attr()' - Find a localized attribute.
503 */
504
505 static ppd_attr_t * /* O - Localized attribute or NULL */
506 ppd_localized_attr(ppd_file_t *ppd, /* I - PPD file */
507 const char *keyword, /* I - Main keyword */
508 const char *spec, /* I - Option keyword */
509 const char *ll_CC, /* I - Language + country locale */
510 const char *ll) /* I - Language locale */
511 {
512 char lkeyword[PPD_MAX_NAME]; /* Localization keyword */
513 ppd_attr_t *attr; /* Current attribute */
514
515
516 DEBUG_printf(("ppd_text(ppd=%p, keyword=\"%s\", spec=\"%s\", "
517 "ll_CC=\"%s\", ll=\"%s\")\n",
518 ppd, keyword, spec, ll_CC, ll));
519
520 /*
521 * Look for Keyword.ll_CC, then Keyword.ll...
522 */
523
524 snprintf(lkeyword, sizeof(lkeyword), "%s.%s", ll_CC, keyword);
525 if ((attr = ppdFindAttr(ppd, lkeyword, spec)) == NULL)
526 {
527 snprintf(lkeyword, sizeof(lkeyword), "%s.%s", ll, keyword);
528 attr = ppdFindAttr(ppd, lkeyword, spec);
529
530 if (!attr)
531 {
532 if (!strcmp(ll, "ja"))
533 {
534 /*
535 * Due to a bug in the CUPS DDK 1.1.0 ppdmerge program, Japanese
536 * PPD files were incorrectly assigned "jp" as the locale name
537 * instead of "ja". Support both the old (incorrect) and new
538 * locale names for Japanese...
539 */
540
541 snprintf(lkeyword, sizeof(lkeyword), "jp.%s", keyword);
542 attr = ppdFindAttr(ppd, lkeyword, spec);
543 }
544 else if (!strcmp(ll, "no"))
545 {
546 /*
547 * Norway has two languages, "Bokmal" (the primary one)
548 * and "Nynorsk" (new Norwegian); we map "no" to "nb" here as
549 * recommended by the locale folks...
550 */
551
552 snprintf(lkeyword, sizeof(lkeyword), "nb.%s", keyword);
553 attr = ppdFindAttr(ppd, lkeyword, spec);
554 }
555 }
556 }
557
558 #ifdef DEBUG
559 if (attr)
560 printf(" *%s %s/%s: \"%s\"\n", attr->name, attr->spec, attr->text,
561 attr->value ? attr->value : "");
562 else
563 puts(" NOT FOUND");
564 #endif /* DEBUG */
565
566 return (attr);
567 }
568
569
570 /*
571 * End of "$Id: localize.c 6882 2007-08-29 21:05:10Z mike $".
572 */