1/* 2 * "$Id: translate.c 11093 2013-07-03 20:48:42Z msweet $" 3 * 4 * HTTP-based translation program for CUPS. 5 * 6 * This program uses Google to translate the CUPS template (cups.pot) to 7 * several different languages. The translation isn't perfect, but it's 8 * a start (better than working from scratch.) 9 * 10 * Copyright 2007-2010 by Apple Inc. 11 * Copyright 1997-2006 by Easy Software Products. 12 * 13 * These coded instructions, statements, and computer programs are the 14 * property of Apple Inc. and are protected by Federal copyright 15 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 16 * which should have been included with this file. If this file is 17 * file is missing or damaged, see the license at "http://www.cups.org/". 18 * 19 * Contents: 20 * 21 * main() - Main entry. 22 * save_messages() - Save messages to a .po file. 23 * translate_messages() - Translate messages using Google. 24 * write_string() - Write a quoted string to a file. 25 */ 26 27/* 28 * Include necessary headers... 29 */ 30 31#include <cups/cups-private.h> 32#include <unistd.h> 33 34 35/* 36 * Local functions... 37 */ 38 39int save_messages(cups_array_t *cat, const char *filename); 40int translate_messages(cups_array_t *cat, const char *lang); 41int write_string(cups_file_t *fp, const char *s); 42 43 44/* 45 * 'main()' - Main entry. 46 */ 47 48int /* O - Exit status */ 49main(int argc, /* I - Number of command-line arguments */ 50 char *argv[]) /* I - Command-line arguments */ 51{ 52 cups_array_t *cat; /* Message catalog */ 53 54 55 if (argc != 3) 56 { 57 fputs("Usage: translate cups_language.po language\n", stderr); 58 return (1); 59 } 60 61 if (access(argv[1], 0)) 62 cat = _cupsMessageLoad("cups.pot", 1); 63 else 64 cat = _cupsMessageLoad(argv[1], 1); 65 66 if (!cat) 67 { 68 puts("Unable to load message catalog."); 69 return (1); 70 } 71 72 if (!translate_messages(cat, argv[2])) 73 { 74 puts("Unable to translate message catalog."); 75 return (1); 76 } 77 78 if (!save_messages(cat, argv[1])) 79 { 80 puts("Unable to save message catalog."); 81 return (1); 82 } 83 84 return (0); 85} 86 87 88/* 89 * 'save_messages()' - Save messages to a .po file. 90 */ 91 92int /* O - 1 on success, 0 on error */ 93save_messages(cups_array_t *cat, /* I - Message catalog */ 94 const char *filename) /* I - File to save to */ 95{ 96 _cups_message_t *m; /* Current message */ 97 cups_file_t *fp; /* File pointer */ 98 99 100 /* 101 * Open the message catalog... 102 */ 103 104 if ((fp = cupsFileOpen(filename, "w")) == NULL) 105 return (0); 106 107 /* 108 * Save the messages to a file... 109 */ 110 111 for (m = (_cups_message_t *)cupsArrayFirst(cat); 112 m; 113 m = (_cups_message_t *)cupsArrayNext(cat)) 114 { 115 if (cupsFilePuts(fp, "msgid \"") < 0) 116 break; 117 118 if (!write_string(fp, m->id)) 119 break; 120 121 if (cupsFilePuts(fp, "\"\nmsgstr \"") < 0) 122 break; 123 124 if (m->str) 125 { 126 if (!write_string(fp, m->str)) 127 break; 128 } 129 130 if (cupsFilePuts(fp, "\"\n") < 0) 131 break; 132 } 133 134 cupsFileClose(fp); 135 136 return (!m); 137} 138 139 140/* 141 * 'translate_messages()' - Translate messages using Google. 142 */ 143 144int /* O - 1 on success, 0 on error */ 145translate_messages(cups_array_t *cat, /* I - Message catalog */ 146 const char *lang) /* I - Output language... */ 147{ 148 /* 149 * Google provides a simple translation/language tool for translating 150 * from one language to another. It is far from perfect, however it 151 * can be used to get a basic translation done or update an existing 152 * translation when no other resources are available. 153 * 154 * Translation requests are sent as HTTP POSTs to 155 * "http://translate.google.com/translate_t" with the following form 156 * variables: 157 * 158 * Name Description Value 159 * -------- ---------------------------------- ---------------- 160 * hl Help language? "en" 161 * ie Input encoding "UTF8" 162 * langpair Language pair "en|" + language 163 * oe Output encoding "UTF8" 164 * text Text to translate translation string 165 */ 166 167 int ret; /* Return value */ 168 _cups_message_t *m; /* Current message */ 169 int tries; /* Number of tries... */ 170 http_t *http; /* HTTP connection */ 171 http_status_t status; /* Status of POST request */ 172 char *idptr, /* Pointer into msgid */ 173 buffer[65536], /* Input/output buffer */ 174 *bufptr, /* Pointer into buffer */ 175 *bufend, /* Pointer to end of buffer */ 176 length[16]; /* Content length */ 177 int bytes; /* Number of bytes read */ 178 179 180 /* 181 * Connect to translate.google.com... 182 */ 183 184 puts("Connecting to translate.google.com..."); 185 186 if ((http = httpConnect("translate.google.com", 80)) == NULL) 187 { 188 perror("Unable to connect to translate.google.com"); 189 return (0); 190 } 191 192 /* 193 * Scan the current messages, requesting a translation of any untranslated 194 * messages... 195 */ 196 197 for (m = (_cups_message_t *)cupsArrayFirst(cat), ret = 1; 198 m; 199 m = (_cups_message_t *)cupsArrayNext(cat)) 200 { 201 /* 202 * Skip messages that are already translated... 203 */ 204 205 if (m->str && m->str[0]) 206 continue; 207 208 /* 209 * Encode the form data into the buffer... 210 */ 211 212 snprintf(buffer, sizeof(buffer), 213 "hl=en&ie=UTF8&langpair=en|%s&oe=UTF8&text=", lang); 214 bufptr = buffer + strlen(buffer); 215 bufend = buffer + sizeof(buffer) - 5; 216 217 for (idptr = m->id; *idptr && bufptr < bufend; idptr ++) 218 if (*idptr == ' ') 219 *bufptr++ = '+'; 220 else if (*idptr < ' ' || *idptr == '%') 221 { 222 sprintf(bufptr, "%%%02X", *idptr & 255); 223 bufptr += 3; 224 } 225 else if (*idptr != '&') 226 *bufptr++ = *idptr; 227 228 *bufptr++ = '&'; 229 *bufptr = '\0'; 230 231 sprintf(length, "%d", (int)(bufptr - buffer)); 232 233 /* 234 * Send the request... 235 */ 236 237 printf("\"%s\" = ", m->id); 238 fflush(stdout); 239 240 tries = 0; 241 242 do 243 { 244 httpClearFields(http); 245 httpSetField(http, HTTP_FIELD_CONTENT_TYPE, 246 "application/x-www-form-urlencoded"); 247 httpSetField(http, HTTP_FIELD_CONTENT_LENGTH, length); 248 249 if (httpPost(http, "/translate_t")) 250 { 251 httpReconnect(http); 252 httpPost(http, "/translate_t"); 253 } 254 255 httpWrite2(http, buffer, bufptr - buffer); 256 257 while ((status = httpUpdate(http)) == HTTP_CONTINUE); 258 259 if (status != HTTP_OK && status != HTTP_ERROR) 260 httpFlush(http); 261 262 tries ++; 263 } 264 while (status == HTTP_ERROR && tries < 10); 265 266 if (status == HTTP_OK) 267 { 268 /* 269 * OK, read the translation back... 270 */ 271 272 bufptr = buffer; 273 bufend = buffer + sizeof(buffer) - 1; 274 275 while ((bytes = httpRead2(http, bufptr, bufend - bufptr)) > 0) 276 bufptr += bytes; 277 278 if (bytes < 0) 279 { 280 /* 281 * Read error, abort! 282 */ 283 284 puts("READ ERROR!"); 285 ret = 0; 286 break; 287 } 288 289 *bufptr = '\0'; 290 291 /* 292 * Find the div containing translation 293 */ 294 295 if ((bufptr = strstr(buffer, "<div id=result_box")) == NULL) 296 { 297 /* 298 * No textarea, abort! 299 */ 300 301 puts("NO div id=result_box!"); 302 ret = 0; 303 break; 304 } 305 306 if ((bufptr = strchr(bufptr, '>')) == NULL) 307 { 308 /* 309 * textarea doesn't end, abort! 310 */ 311 312 puts("DIV SHORT DATA!"); 313 ret = 0; 314 break; 315 } 316 317 bufptr ++; 318 319 if ((bufend = strstr(bufptr, "</div>")) == NULL) 320 { 321 /* 322 * textarea doesn't close, abort! 323 */ 324 325 puts("/DIV SHORT DATA!"); 326 ret = 0; 327 break; 328 } 329 330 *bufend = '\0'; 331 332 /* 333 * Copy the translation... 334 */ 335 336 m->str = strdup(bufptr); 337 338 /* 339 * Convert character entities to regular chars... 340 */ 341 342 for (bufptr = strchr(m->str, '&'); 343 bufptr; 344 bufptr = strchr(bufptr + 1, '&')) 345 { 346 if (!strncmp(bufptr, "<", 4)) 347 { 348 *bufptr = '<'; 349 _cups_strcpy(bufptr + 1, bufptr + 4); 350 } 351 else if (!strncmp(bufptr, ">", 4)) 352 { 353 *bufptr = '>'; 354 _cups_strcpy(bufptr + 1, bufptr + 4); 355 } 356 else if (!strncmp(bufptr, "&", 5)) 357 _cups_strcpy(bufptr + 1, bufptr + 5); 358 } 359 360 printf("\"%s\"\n", m->str); 361 } 362 else if (status == HTTP_ERROR) 363 { 364 printf("NETWORK ERROR (%s)!\n", strerror(httpError(http))); 365 ret = 0; 366 break; 367 } 368 else 369 { 370 printf("HTTP ERROR %d!\n", status); 371 ret = 0; 372 break; 373 } 374 } 375 376 httpClose(http); 377 378 return (ret); 379} 380 381 382/* 383 * 'write_string()' - Write a quoted string to a file. 384 */ 385 386int /* O - 1 on success, 0 on failure */ 387write_string(cups_file_t *fp, /* I - File to write to */ 388 const char *s) /* I - String */ 389{ 390 while (*s) 391 { 392 switch (*s) 393 { 394 case '\n' : 395 if (cupsFilePuts(fp, "\\n") < 0) 396 return (0); 397 break; 398 399 case '\r' : 400 if (cupsFilePuts(fp, "\\r") < 0) 401 return (0); 402 break; 403 404 case '\t' : 405 if (cupsFilePuts(fp, "\\t") < 0) 406 return (0); 407 break; 408 409 case '\\' : 410 if (cupsFilePuts(fp, "\\\\") < 0) 411 return (0); 412 break; 413 414 case '\"' : 415 if (cupsFilePuts(fp, "\\\"") < 0) 416 return (0); 417 break; 418 419 default : 420 if ((*s & 255) < ' ') 421 { 422 if (cupsFilePrintf(fp, "\\%o", *s) < 0) 423 return (0); 424 } 425 else if (cupsFilePutChar(fp, *s) < 0) 426 return (0); 427 break; 428 } 429 430 s ++; 431 } 432 433 return (1); 434} 435 436 437/* 438 * End of "$Id: translate.c 11093 2013-07-03 20:48:42Z msweet $". 439 */ 440