Revert manifest to default one
[external/cups.git] / locale / translate.c
1 /*
2  * "$Id: translate.c 9048 2010-03-24 08:07:15Z mike $"
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
39 int     save_messages(cups_array_t *cat, const char *filename);
40 int     translate_messages(cups_array_t *cat, const char *lang);
41 int     write_string(cups_file_t *fp, const char *s);
42
43
44 /*
45  * 'main()' - Main entry.
46  */
47
48 int                                     /* O - Exit status */
49 main(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
92 int                                     /* O - 1 on success, 0 on error */
93 save_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
144 int                                     /* O - 1 on success, 0 on error */
145 translate_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
386 int                                     /* O - 1 on success, 0 on failure */
387 write_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 9048 2010-03-24 08:07:15Z mike $".
439  */