]> git.ipfire.org Git - thirdparty/cups.git/blame - cups/localize.c
Import CUPS 1.4svn r7023 into easysw/current.
[thirdparty/cups.git] / cups / localize.c
CommitLineData
fa73b229 1/*
2e4ff8af 2 * "$Id: localize.c 6882 2007-08-29 21:05:10Z mike $"
fa73b229 3 *
4 * PPD custom option routines for the Common UNIX Printing System (CUPS).
5 *
bc44d920 6 * Copyright 2007 by Apple Inc.
7594b224 7 * Copyright 1997-2007 by Easy Software Products, all rights reserved.
fa73b229 8 *
9 * These coded instructions, statements, and computer programs are the
bc44d920 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/".
fa73b229 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 *
bc44d920 28 * ppdLocalize() - Localize the PPD file to the current locale.
29 * ppdLocalizeIPPReason() - Get the localized version of a cupsIPPReason
30 * attribute.
31 * ppd_ll_CC() - Get the current locale names.
32 * ppd_localized_attr() - Find a localized attribute.
fa73b229 33 */
34
35/*
36 * Include necessary headers.
37 */
38
39#include "globals.h"
40#include "debug.h"
41
42
43/*
44 * Local functions...
45 */
46
db1f069b
MS
47static void ppd_ll_CC(char *ll_CC, int ll_CC_size,
48 char *ll, int ll_size);
bc44d920 49static ppd_attr_t *ppd_localized_attr(ppd_file_t *ppd,
50 const char *keyword,
51 const char *spec, const char *ll_CC,
52 const char *ll);
fa73b229 53
54
55/*
56 * 'ppdLocalize()' - Localize the PPD file to the current locale.
89d46774 57 *
bc44d920 58 * All groups, options, and choices are localized, as are ICC profile
59 * descriptions, printer presets, and custom option parameters. Each
60 * localized string uses the UTF-8 character encoding.
61 *
89d46774 62 * @since CUPS 1.2@
fa73b229 63 */
64
65int /* O - 0 on success, -1 on error */
66ppdLocalize(ppd_file_t *ppd) /* I - PPD file */
67{
68 int i, j, k; /* Looping vars */
69 ppd_group_t *group; /* Current group */
70 ppd_option_t *option; /* Current option */
71 ppd_choice_t *choice; /* Current choice */
72 ppd_coption_t *coption; /* Current custom option */
73 ppd_cparam_t *cparam; /* Current custom parameter */
bc44d920 74 ppd_attr_t *attr, /* Current attribute */
75 *locattr; /* Localized attribute */
fa73b229 76 char ckeyword[PPD_MAX_NAME], /* Custom keyword */
77 ll_CC[6], /* Language + country locale */
78 ll[3]; /* Language locale */
fa73b229 79
80
81 /*
82 * Range check input...
83 */
84
d09495fa 85 DEBUG_printf(("ppdLocalize(ppd=%p)\n", ppd));
86
fa73b229 87 if (!ppd)
88 return (-1);
89
90 /*
91 * Get the default language...
92 */
93
db1f069b 94 ppd_ll_CC(ll_CC, sizeof(ll_CC), ll, sizeof(ll));
d09495fa 95
fa73b229 96 /*
97 * Now lookup all of the groups, options, choices, etc.
98 */
99
100 for (i = ppd->num_groups, group = ppd->groups; i > 0; i --, group ++)
101 {
bc44d920 102 if ((locattr = ppd_localized_attr(ppd, "Translation", group->name,
103 ll_CC, ll)) != NULL)
104 strlcpy(group->text, locattr->text, sizeof(group->text));
fa73b229 105
106 for (j = group->num_options, option = group->options; j > 0; j --, option ++)
107 {
bc44d920 108 if ((locattr = ppd_localized_attr(ppd, "Translation", option->keyword,
109 ll_CC, ll)) != NULL)
110 strlcpy(option->text, locattr->text, sizeof(option->text));
fa73b229 111
112 for (k = option->num_choices, choice = option->choices;
113 k > 0;
114 k --, choice ++)
115 {
116 if (strcmp(choice->choice, "Custom"))
bc44d920 117 locattr = ppd_localized_attr(ppd, option->keyword, choice->choice,
118 ll_CC, ll);
fa73b229 119 else
120 {
121 snprintf(ckeyword, sizeof(ckeyword), "Custom%s", option->keyword);
122
bc44d920 123 locattr = ppd_localized_attr(ppd, ckeyword, "True", ll_CC, ll);
fa73b229 124 }
125
bc44d920 126 if (locattr)
127 strlcpy(choice->text, locattr->text, sizeof(choice->text));
fa73b229 128 }
129 }
130 }
131
132 /*
133 * Translate any custom parameters...
134 */
135
136 for (coption = (ppd_coption_t *)cupsArrayFirst(ppd->coptions);
137 coption;
138 coption = (ppd_coption_t *)cupsArrayNext(ppd->coptions))
139 {
140 for (cparam = (ppd_cparam_t *)cupsArrayFirst(coption->params);
141 cparam;
142 cparam = (ppd_cparam_t *)cupsArrayNext(coption->params))
143 {
144 snprintf(ckeyword, sizeof(ckeyword), "ParamCustom%s", coption->keyword);
145
bc44d920 146 if ((locattr = ppd_localized_attr(ppd, ckeyword, cparam->name,
147 ll_CC, ll)) != NULL)
148 strlcpy(cparam->text, locattr->text, sizeof(cparam->text));
fa73b229 149 }
150 }
151
f42414bf 152 /*
153 * Translate ICC profile names...
154 */
155
156 if ((attr = ppdFindAttr(ppd, "APCustomColorMatchingName", NULL)) != NULL)
157 {
bc44d920 158 if ((locattr = ppd_localized_attr(ppd, "APCustomColorMatchingName",
159 attr->spec, ll_CC, ll)) != NULL)
160 strlcpy(attr->text, locattr->text, sizeof(attr->text));
f42414bf 161 }
162
163 for (attr = ppdFindAttr(ppd, "cupsICCProfile", NULL);
164 attr;
165 attr = ppdFindNextAttr(ppd, "cupsICCProfile", NULL))
166 {
167 cupsArraySave(ppd->sorted_attrs);
168
bc44d920 169 if ((locattr = ppd_localized_attr(ppd, "cupsICCProfile", attr->spec,
170 ll_CC, ll)) != NULL)
171 strlcpy(attr->text, locattr->text, sizeof(attr->text));
f42414bf 172
173 cupsArrayRestore(ppd->sorted_attrs);
174 }
175
09a101d6 176 /*
177 * Translate printer presets...
178 */
179
180 for (attr = ppdFindAttr(ppd, "APPrinterPreset", NULL);
181 attr;
182 attr = ppdFindNextAttr(ppd, "APPrinterPreset", NULL))
183 {
184 cupsArraySave(ppd->sorted_attrs);
185
bc44d920 186 if ((locattr = ppd_localized_attr(ppd, "APPrinterPreset", attr->spec,
187 ll_CC, ll)) != NULL)
188 strlcpy(attr->text, locattr->text, sizeof(attr->text));
09a101d6 189
190 cupsArrayRestore(ppd->sorted_attrs);
191 }
192
fa73b229 193 return (0);
194}
195
196
197/*
bc44d920 198 * 'ppdLocalizeIPPReason()' - Get the localized version of a cupsIPPReason
199 * attribute.
200 *
201 * This function uses the current locale to find the corresponding reason
202 * text or URI from the attribute value. If "scheme" is NULL or "text",
203 * the returned value contains human-readable (UTF-8) text from the translation
204 * string or attribute value. Otherwise the corresponding URI is returned.
205 *
206 * If no value of the requested scheme can be found, NULL is returned.
207 *
208 * @since CUPS 1.3@
209 */
210
211const char * /* O - Value or NULL if not found */
212ppdLocalizeIPPReason(
213 ppd_file_t *ppd, /* I - PPD file */
214 const char *reason, /* I - IPP reason keyword to look up */
215 const char *scheme, /* I - URI scheme or NULL for text */
216 char *buffer, /* I - Value buffer */
217 size_t bufsize) /* I - Size of value buffer */
218{
219 ppd_attr_t *locattr; /* Localized attribute */
220 char ll_CC[6], /* Language + country locale */
221 ll[3], /* Language locale */
222 *bufptr, /* Pointer into buffer */
223 *bufend, /* Pointer to end of buffer */
224 *valptr; /* Pointer into value */
225 int ch, /* Hex-encoded character */
226 schemelen; /* Length of scheme name */
227
228
229 /*
230 * Range check input...
231 */
232
233 if (buffer)
234 *buffer = '\0';
235
236 if (!ppd || !reason || (scheme && !*scheme) ||
237 !buffer || bufsize < PPD_MAX_TEXT)
238 return (NULL);
239
240 /*
241 * Get the default language...
242 */
243
db1f069b 244 ppd_ll_CC(ll_CC, sizeof(ll_CC), ll, sizeof(ll));
bc44d920 245
246 /*
247 * Find the localized attribute...
248 */
249
250 if ((locattr = ppd_localized_attr(ppd, "cupsIPPReason", reason,
251 ll_CC, ll)) == NULL)
252 locattr = ppdFindAttr(ppd, "cupsIPPReason", reason);
253
254 if (!locattr)
255 return (NULL);
256
257 /*
258 * Now find the value we need...
259 */
260
261 bufend = buffer + bufsize - 1;
262
263 if (!scheme || !strcmp(scheme, "text"))
264 {
265 /*
266 * Copy a text value (either the translation text or text:... URIs from
267 * the value...
268 */
269
270 strlcpy(buffer, locattr->text, bufsize);
271
272 for (valptr = locattr->value, bufptr = buffer; *valptr && bufptr < bufend;)
273 {
274 if (!strncmp(valptr, "text:", 5))
275 {
276 /*
277 * Decode text: URI and add to the buffer...
278 */
279
280 if (bufptr > buffer)
281 *bufptr++ = ' '; /* Add leading whitespace */
282
283 valptr += 5;
284
285 while (*valptr && !isspace(*valptr & 255) && bufptr < bufend)
286 {
287 if (*valptr == '%' && isxdigit(valptr[1] & 255) &&
288 isxdigit(valptr[2] & 255))
289 {
290 /*
291 * Pull a hex-encoded character from the URI...
292 */
293
294 valptr ++;
295
296 if (isdigit(*valptr & 255))
297 ch = (*valptr - '0') << 4;
298 else
299 ch = (tolower(*valptr) - 'a' + 10) << 4;
300 valptr ++;
301
302 if (isdigit(*valptr & 255))
303 *bufptr++ = ch | (*valptr - '0');
304 else
305 *bufptr++ = ch | (tolower(*valptr) - 'a' + 10);
306 valptr ++;
307 }
308 else if (*valptr == '+')
309 {
310 *bufptr++ = ' ';
311 valptr ++;
312 }
313 else
314 *bufptr++ = *valptr++;
315 }
316 }
317 else
318 {
319 /*
320 * Skip this URI...
321 */
322
323 while (*valptr && !isspace(*valptr & 255))
324 valptr++;
325 }
326
327 /*
328 * Skip whitespace...
329 */
330
331 while (isspace(*valptr & 255))
332 valptr ++;
333 }
334
335 if (bufptr > buffer)
336 *bufptr = '\0';
337
338 return (buffer);
339 }
340 else
341 {
342 /*
343 * Copy a URI...
344 */
345
346 schemelen = strlen(scheme);
347 if (scheme[schemelen - 1] == ':') /* Force scheme to be just the name */
348 schemelen --;
349
350 for (valptr = locattr->value, bufptr = buffer; *valptr && bufptr < bufend;)
351 {
352 if ((!strncmp(valptr, scheme, schemelen) && valptr[schemelen] == ':') ||
353 (*valptr == '/' && !strcmp(scheme, "file")))
354 {
355 /*
356 * Copy URI...
357 */
358
359 while (*valptr && !isspace(*valptr & 255) && bufptr < bufend)
360 *bufptr++ = *valptr++;
361
362 *bufptr = '\0';
363
364 return (buffer);
365 }
366 else
367 {
368 /*
369 * Skip this URI...
370 */
371
372 while (*valptr && !isspace(*valptr & 255))
373 valptr++;
374 }
375
376 /*
377 * Skip whitespace...
378 */
379
380 while (isspace(*valptr & 255))
381 valptr ++;
382 }
383
384 return (NULL);
385 }
386}
387
388
389/*
390 * 'ppd_ll_CC()' - Get the current locale names.
391 */
392
393static void
394ppd_ll_CC(char *ll_CC, /* O - Country-specific locale name */
db1f069b
MS
395 int ll_CC_size, /* I - Size of country-specific name */
396 char *ll, /* O - Generic locale name */
397 int ll_size) /* I - Size of generic name */
bc44d920 398{
399 cups_lang_t *lang; /* Current language */
400
401
402 /*
403 * Get the current locale...
404 */
405
406 if ((lang = cupsLangDefault()) == NULL)
407 {
db1f069b
MS
408 strlcpy(ll_CC, "en_US", ll_CC_size);
409 strlcpy(ll, "en", ll_size);
bc44d920 410 return;
411 }
412
413 /*
414 * Copy the locale name...
415 */
416
db1f069b
MS
417 strlcpy(ll_CC, lang->language, ll_CC_size);
418 strlcpy(ll, lang->language, ll_size);
419
420 DEBUG_printf(("ll_CC=\"%s\", ll=\"%s\"\n", ll_CC, ll));
bc44d920 421
422 if (strlen(ll_CC) == 2)
423 {
424 /*
425 * Map "ll" to primary/origin country locales to have the best
426 * chance of finding a match...
427 */
428
429 if (!strcmp(ll_CC, "cs"))
db1f069b 430 strlcpy(ll_CC, "cs_CZ", ll_CC_size);
bc44d920 431 else if (!strcmp(ll_CC, "en"))
db1f069b 432 strlcpy(ll_CC, "en_US", ll_CC_size);
bc44d920 433 else if (!strcmp(ll_CC, "ja"))
db1f069b 434 strlcpy(ll_CC, "ja_JP", ll_CC_size);
bc44d920 435 else if (!strcmp(ll_CC, "sv"))
db1f069b
MS
436 strlcpy(ll_CC, "sv_SE", ll_CC_size);
437 else if (!strcmp(ll_CC, "zh")) /* Simplified Chinese */
438 strlcpy(ll_CC, "zh_CN", ll_CC_size);
439 else if (ll_CC_size >= 6)
bc44d920 440 {
441 ll_CC[2] = '_';
442 ll_CC[3] = toupper(ll_CC[0] & 255);
443 ll_CC[4] = toupper(ll_CC[1] & 255);
444 ll_CC[5] = '\0';
445 }
446 }
447
448 DEBUG_printf(("ppd_ll_CC: lang->language=\"%s\", ll=\"%s\", ll_CC=\"%s\"...\n",
449 lang->language, ll, ll_CC));
450}
451
452
453/*
454 * 'ppd_localized_attr()' - Find a localized attribute.
fa73b229 455 */
456
bc44d920 457static ppd_attr_t * /* O - Localized attribute or NULL */
458ppd_localized_attr(ppd_file_t *ppd, /* I - PPD file */
459 const char *keyword, /* I - Main keyword */
460 const char *spec, /* I - Option keyword */
461 const char *ll_CC, /* I - Language + country locale */
462 const char *ll) /* I - Language locale */
fa73b229 463{
464 char lkeyword[PPD_MAX_NAME]; /* Localization keyword */
465 ppd_attr_t *attr; /* Current attribute */
466
467
d09495fa 468 DEBUG_printf(("ppd_text(ppd=%p, keyword=\"%s\", spec=\"%s\", "
469 "ll_CC=\"%s\", ll=\"%s\")\n",
470 ppd, keyword, spec, ll_CC, ll));
471
fa73b229 472 /*
473 * Look for Keyword.ll_CC, then Keyword.ll...
474 */
475
d09495fa 476 snprintf(lkeyword, sizeof(lkeyword), "%s.%s", ll_CC, keyword);
fa73b229 477 if ((attr = ppdFindAttr(ppd, lkeyword, spec)) == NULL)
478 {
d09495fa 479 snprintf(lkeyword, sizeof(lkeyword), "%s.%s", ll, keyword);
fa73b229 480 attr = ppdFindAttr(ppd, lkeyword, spec);
7594b224 481
db1f069b 482 if (!attr)
7594b224 483 {
db1f069b
MS
484 if (!strcmp(ll, "ja"))
485 {
486 /*
487 * Due to a bug in the CUPS DDK 1.1.0 ppdmerge program, Japanese
488 * PPD files were incorrectly assigned "jp" as the locale name
489 * instead of "ja". Support both the old (incorrect) and new
490 * locale names for Japanese...
491 */
7594b224 492
db1f069b
MS
493 snprintf(lkeyword, sizeof(lkeyword), "jp.%s", keyword);
494 attr = ppdFindAttr(ppd, lkeyword, spec);
495 }
496 else if (!strcmp(ll, "no"))
497 {
498 /*
499 * Norway has two languages, "Bokmal" (the primary one)
500 * and "Nynorsk" (new Norwegian); we map "no" to "nb" here as
501 * recommended by the locale folks...
502 */
503
504 snprintf(lkeyword, sizeof(lkeyword), "nb.%s", keyword);
505 attr = ppdFindAttr(ppd, lkeyword, spec);
506 }
7594b224 507 }
fa73b229 508 }
509
d09495fa 510#ifdef DEBUG
511 if (attr)
512 printf(" *%s %s/%s: \"%s\"\n", attr->name, attr->spec, attr->text,
513 attr->value ? attr->value : "");
514 else
515 puts(" NOT FOUND");
516#endif /* DEBUG */
517
bc44d920 518 return (attr);
fa73b229 519}
520
521
522/*
2e4ff8af 523 * End of "$Id: localize.c 6882 2007-08-29 21:05:10Z mike $".
fa73b229 524 */