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