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, "&lt;", 4))
347	{
348	  *bufptr = '<';
349	  _cups_strcpy(bufptr + 1, bufptr + 4);
350	}
351        else if (!strncmp(bufptr, "&gt;", 4))
352	{
353	  *bufptr = '>';
354	  _cups_strcpy(bufptr + 1, bufptr + 4);
355	}
356        else if (!strncmp(bufptr, "&amp;", 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