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