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