summaryrefslogtreecommitdiff
path: root/development/translation
authorzecke <zecke>2003-04-20 17:24:50 (UTC)
committer zecke <zecke>2003-04-20 17:24:50 (UTC)
commit92c687d281f69085436a77efb8cd1d4d2d9333f8 (patch) (unidiff)
tree7bbfac3ea310ed140e78cb3c17f8219294cb61d0 /development/translation
parentf1f4e6794507d9b8dafb46ce05968a0647a41777 (diff)
downloadopie-92c687d281f69085436a77efb8cd1d4d2d9333f8.zip
opie-92c687d281f69085436a77efb8cd1d4d2d9333f8.tar.gz
opie-92c687d281f69085436a77efb8cd1d4d2d9333f8.tar.bz2
Initial revision
Diffstat (limited to 'development/translation') (more/less context) (ignore whitespace changes)
-rw-r--r--development/translation/opie-lrelease/lrelease.pro16
-rw-r--r--development/translation/opie-lrelease/main.cpp150
-rw-r--r--development/translation/opie-lupdate/fetchtr.cpp784
-rwxr-xr-xdevelopment/translation/opie-lupdate/lupdatebin0 -> 128093 bytes
-rw-r--r--development/translation/opie-lupdate/main.cpp215
-rw-r--r--development/translation/opie-lupdate/merge.cpp115
-rw-r--r--development/translation/opie-lupdate/numberh.cpp235
-rw-r--r--development/translation/opie-lupdate/opie-lupdate.pro21
-rw-r--r--development/translation/opie-lupdate/sametexth.cpp84
-rw-r--r--development/translation/shared/metatranslator.cpp586
-rw-r--r--development/translation/shared/metatranslator.h99
-rw-r--r--development/translation/shared/opie.cpp40
-rw-r--r--development/translation/shared/opie.h21
-rw-r--r--development/translation/shared/proparser.cpp87
-rw-r--r--development/translation/shared/proparser.h29
15 files changed, 2482 insertions, 0 deletions
diff --git a/development/translation/opie-lrelease/lrelease.pro b/development/translation/opie-lrelease/lrelease.pro
new file mode 100644
index 0000000..fa285cb
--- a/dev/null
+++ b/development/translation/opie-lrelease/lrelease.pro
@@ -0,0 +1,16 @@
1 TEMPLATE= app
2 CONFIG += qt warn_on console
3 HEADERS = ../shared/metatranslator.h \
4 ../shared/proparser.h \
5 ../shared/opie.h
6 SOURCES = main.cpp \
7 ../shared/metatranslator.cpp \
8 ../shared/proparser.cpp \
9 ../shared/opie.cpp
10
11 DEFINES += QT_INTERNAL_XML
12
13 TARGET = opie-lrelease
14 INCLUDEPATH+= ../shared
15 #DESTDIR = ../../../bin
16
diff --git a/development/translation/opie-lrelease/main.cpp b/development/translation/opie-lrelease/main.cpp
new file mode 100644
index 0000000..6008c4e
--- a/dev/null
+++ b/development/translation/opie-lrelease/main.cpp
@@ -0,0 +1,150 @@
1/**********************************************************************
2** Copyright (C) 2000 Trolltech AS. All rights reserved.
3**
4** This file is part of Qt Linguist.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20
21#include <metatranslator.h>
22#include <proparser.h>
23#include <opie.h>
24
25#include <qfile.h>
26#include <qregexp.h>
27#include <qstring.h>
28#include <qstringlist.h>
29#include <qtextstream.h>
30
31#include <errno.h>
32
33typedef QValueList<MetaTranslatorMessage> TML;
34
35static void printUsage()
36{
37 fprintf( stderr, "Usage:\n"
38 " lrelease [options] project-file\n"
39 " lrelease [options] ts-files\n"
40 "Options:\n"
41 " -opie OPIE dir overrides $OPIEDIR\n"
42 " -help Display this information and exit\n"
43 " -verbose\n"
44 " Explain what is being done\n"
45 " -version\n"
46 " Display the version of lrelease and exit\n" );
47}
48static void releaseQmFile( const QString& tsFileName, bool verbose )
49{
50 MetaTranslator tor;
51 QString qmFileName = tsFileName;
52 qmFileName.replace( QRegExp("\\.ts$"), "" );
53 qmFileName += ".qm";
54
55 if ( tor.load(tsFileName) ) {
56 if ( verbose )
57 fprintf( stderr, "Updating '%s'...\n", qmFileName.latin1() );
58 if ( !tor.release(qmFileName, verbose) )
59 fprintf( stderr,
60 "lrelease warning: For some reason, I cannot save '%s'\n",
61 qmFileName.latin1() );
62 } else {
63 fprintf( stderr,
64 "lrelease warning: For some reason, I cannot load '%s'\n",
65 tsFileName.latin1() );
66 }
67}
68static void metaQmFile( const QString &opiedir,
69 const QStringList& lang,
70 const QString& basename,
71 bool isLib, bool verb ) {
72 QString target = basename + ".ts";
73 if ( isLib ) target.prepend("lib");
74
75 for ( QStringList::ConstIterator it = lang.begin(); it != lang.end();
76 ++it ) {
77 QString fileName = opiedir + "/i18n/" + (*it) + "/" + target;
78 qWarning("Target is %s", fileName.latin1() );
79 }
80}
81int main( int argc, char **argv )
82{
83 bool verbose = FALSE;
84 bool metTranslations = FALSE;
85 int numFiles = 0;
86 QString opiedir;
87 QStringList languageList = OPIE::self()->languageList( opiedir );
88
89 for ( int i = 1; i < argc; i++ ) {
90 if ( qstrcmp(argv[i], "-help") == 0 ) {
91 printUsage();
92 return 0;
93 } else if ( qstrcmp(argv[i], "-verbose") == 0 ) {
94 verbose = TRUE;
95 continue;
96 } else if ( qstrcmp(argv[i], "-version") == 0 ) {
97 fprintf( stderr, "lrelease version %s\n", QT_VERSION_STR );
98 return 0;
99 } else if ( qstrcmp(argv[i], "-opie") == 0 ) {
100 if ( i+1 < argc ) {
101 opiedir = argv[i+1];
102 languageList = OPIE::self()->languageList(opiedir);
103 }
104 }
105
106 numFiles++;
107 QFile f( argv[i] );
108 if ( !f.open(IO_ReadOnly) ) {
109 fprintf( stderr,
110 "lrelease error: Cannot open file '%s': %s\n", argv[i],
111 strerror(errno) );
112 return 1;
113 }
114
115 QTextStream t( &f );
116 QString fullText = t.read();
117 f.close();
118
119 if ( fullText.find(QString("<!DOCTYPE TS>")) >= 0 ) {
120 releaseQmFile( argv[i], verbose );
121 } else {
122 QString target;
123 bool isLib = FALSE;
124 QMap<QString, QString> tagMap = proFileTagMap( fullText );
125 QMap<QString, QString>::Iterator it;
126
127 for ( it = tagMap.begin(); it != tagMap.end(); ++it ) {
128 QStringList toks = QStringList::split( ' ', it.data() );
129 QStringList::Iterator t;
130
131 for ( t = toks.begin(); t != toks.end(); ++t ) {
132 if ( it.key() == "TARGET" ) {
133 target = *t;
134 }else if ( it.key() == "TEMPLATE" ) {
135 if ( (*t).stripWhiteSpace().lower() == "lib" )
136 isLib = TRUE;
137 }
138 }
139 }
140 metaQmFile( OPIE::self()->opieDir(opiedir),
141 languageList, target, isLib, verbose );
142 }
143 }
144
145 if ( numFiles == 0 ) {
146 printUsage();
147 return 1;
148 }
149 return 0;
150}
diff --git a/development/translation/opie-lupdate/fetchtr.cpp b/development/translation/opie-lupdate/fetchtr.cpp
new file mode 100644
index 0000000..eb25555
--- a/dev/null
+++ b/development/translation/opie-lupdate/fetchtr.cpp
@@ -0,0 +1,784 @@
1/**********************************************************************
2** Copyright (C) 2000 Trolltech AS. All rights reserved.
3**
4** This file is part of Qt Linguist.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20
21#include <metatranslator.h>
22
23#include <qfile.h>
24#include <qregexp.h>
25#include <qstring.h>
26#include <qtextstream.h>
27#include <qvaluestack.h>
28#include <qxml.h>
29
30#include <ctype.h>
31#include <errno.h>
32#include <stdio.h>
33#include <string.h>
34
35/* qmake ignore Q_OBJECT */
36
37static const char MagicComment[] = "TRANSLATOR ";
38
39static QMap<QCString, int> needs_Q_OBJECT;
40static QMap<QCString, int> lacks_Q_OBJECT;
41
42/*
43 The first part of this source file is the C++ tokenizer. We skip
44 most of C++; the only tokens that interest us are defined here.
45 Thus, the code fragment
46
47 int main()
48 {
49 printf( "Hello, world!\n" );
50 return 0;
51 }
52
53 is broken down into the following tokens (Tok_ omitted):
54
55 Ident Ident LeftParen RightParen
56 LeftBrace
57 Ident LeftParen String RightParen Semicolon
58 return Semicolon
59 RightBrace.
60
61 The 0 doesn't produce any token.
62*/
63
64enum { Tok_Eof, Tok_class, Tok_namespace, Tok_return, Tok_tr,
65 Tok_trUtf8, Tok_translate, Tok_Q_OBJECT, Tok_Ident,
66 Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon,
67 Tok_Gulbrandsen, Tok_LeftBrace, Tok_RightBrace, Tok_LeftParen,
68 Tok_RightParen, Tok_Comma, Tok_Semicolon };
69
70/*
71 The tokenizer maintains the following global variables. The names
72 should be self-explanatory.
73*/
74static QCString yyFileName;
75static int yyCh;
76static char yyIdent[128];
77static size_t yyIdentLen;
78static char yyComment[65536];
79static size_t yyCommentLen;
80static char yyString[16384];
81static size_t yyStringLen;
82static QValueStack<int> yySavedBraceDepth;
83static int yyBraceDepth;
84static int yyParenDepth;
85static int yyLineNo;
86static int yyCurLineNo;
87
88// the file to read from (if reading from a file)
89static FILE *yyInFile;
90
91// the string to read from and current position in the string (otherwise)
92static QString yyInStr;
93static int yyInPos;
94
95static int (*getChar)();
96
97static int getCharFromFile()
98{
99 int c = getc( yyInFile );
100 if ( c == '\n' )
101 yyCurLineNo++;
102 return c;
103}
104
105static int getCharFromString()
106{
107 if ( yyInPos == (int) yyInStr.length() ) {
108 return EOF;
109 } else {
110 return yyInStr[yyInPos++].latin1();
111 }
112}
113
114static void startTokenizer( const char *fileName, int (*getCharFunc)() )
115{
116 yyInPos = 0;
117 getChar = getCharFunc;
118
119 yyFileName = fileName;
120 yyCh = getChar();
121 yySavedBraceDepth.clear();
122 yyBraceDepth = 0;
123 yyParenDepth = 0;
124 yyCurLineNo = 1;
125}
126
127static int getToken()
128{
129 const char tab[] = "abfnrtv";
130 const char backTab[] = "\a\b\f\n\r\t\v";
131 uint n;
132
133 yyIdentLen = 0;
134 yyCommentLen = 0;
135 yyStringLen = 0;
136
137 while ( yyCh != EOF ) {
138 yyLineNo = yyCurLineNo;
139
140 if ( isalpha(yyCh) || yyCh == '_' ) {
141 do {
142 if ( yyIdentLen < sizeof(yyIdent) - 1 )
143 yyIdent[yyIdentLen++] = (char) yyCh;
144 yyCh = getChar();
145 } while ( isalnum(yyCh) || yyCh == '_' );
146 yyIdent[yyIdentLen] = '\0';
147
148 switch ( yyIdent[0] ) {
149 case 'Q':
150 if ( strcmp(yyIdent + 1, "_OBJECT") == 0 ) {
151 return Tok_Q_OBJECT;
152 } else if ( strcmp(yyIdent + 1, "T_TR_NOOP") == 0 ) {
153 return Tok_tr;
154 } else if ( strcmp(yyIdent + 1, "T_TRANSLATE_NOOP") == 0 ) {
155 return Tok_translate;
156 }
157 break;
158 case 'T':
159 // TR() for when all else fails
160 if ( qstricmp(yyIdent + 1, "R") == 0 )
161 return Tok_tr;
162 break;
163 case 'c':
164 if ( strcmp(yyIdent + 1, "lass") == 0 )
165 return Tok_class;
166 break;
167 case 'n':
168 if ( strcmp(yyIdent + 1, "amespace") == 0 )
169 return Tok_namespace;
170 break;
171 case 'r':
172 if ( strcmp(yyIdent + 1, "eturn") == 0 )
173 return Tok_return;
174 break;
175 case 's':
176 if ( strcmp(yyIdent + 1, "truct") == 0 )
177 return Tok_class;
178 break;
179 case 't':
180 if ( strcmp(yyIdent + 1, "r") == 0 ) {
181 return Tok_tr;
182 } else if ( qstrcmp(yyIdent + 1, "rUtf8") == 0 ) {
183 return Tok_trUtf8;
184 } else if ( qstrcmp(yyIdent + 1, "ranslate") == 0 ) {
185 return Tok_translate;
186 }
187 }
188 return Tok_Ident;
189 } else {
190 switch ( yyCh ) {
191 case '#':
192 /*
193 Early versions of lupdate complained about
194 unbalanced braces in the following code:
195
196 #ifdef ALPHA
197 while ( beta ) {
198 #else
199 while ( gamma ) {
200 #endif
201 delta;
202 }
203
204 The code contains, indeed, two opening braces for
205 one closing brace; yet there's no reason to panic.
206
207 The solution is to remember yyBraceDepth as it was
208 when #if, #ifdef or #ifndef was met, and to set
209 yyBraceDepth to that value when meeting #elif or
210 #else.
211 */
212 do {
213 yyCh = getChar();
214 } while ( isspace(yyCh) && yyCh != '\n' );
215
216 switch ( yyCh ) {
217 case 'i':
218 yyCh = getChar();
219 if ( yyCh == 'f' ) {
220 // if, ifdef, ifndef
221 yySavedBraceDepth.push( yyBraceDepth );
222 }
223 break;
224 case 'e':
225 yyCh = getChar();
226 if ( yyCh == 'l' ) {
227 // elif, else
228 if ( !yySavedBraceDepth.isEmpty() )
229 yyBraceDepth = yySavedBraceDepth.top();
230 } else if ( yyCh == 'n' ) {
231 // endif
232 if ( !yySavedBraceDepth.isEmpty() )
233 yySavedBraceDepth.pop();
234 }
235 }
236 while ( isalnum(yyCh) || yyCh == '_' )
237 yyCh = getChar();
238 break;
239 case '/':
240 yyCh = getChar();
241 if ( yyCh == '/' ) {
242 do {
243 yyCh = getChar();
244 } while ( yyCh != EOF && yyCh != '\n' );
245 } else if ( yyCh == '*' ) {
246 bool metAster = FALSE;
247 bool metAsterSlash = FALSE;
248
249 while ( !metAsterSlash ) {
250 yyCh = getChar();
251 if ( yyCh == EOF ) {
252 fprintf( stderr,
253 "%s: Unterminated C++ comment starting at"
254 " line %d\n",
255 (const char *) yyFileName, yyLineNo );
256 yyComment[yyCommentLen] = '\0';
257 return Tok_Comment;
258 }
259 if ( yyCommentLen < sizeof(yyComment) - 1 )
260 yyComment[yyCommentLen++] = (char) yyCh;
261
262 if ( yyCh == '*' )
263 metAster = TRUE;
264 else if ( metAster && yyCh == '/' )
265 metAsterSlash = TRUE;
266 else
267 metAster = FALSE;
268 }
269 yyCh = getChar();
270 yyCommentLen -= 2;
271 yyComment[yyCommentLen] = '\0';
272 return Tok_Comment;
273 }
274 break;
275 case '"':
276 yyCh = getChar();
277
278 while ( yyCh != EOF && yyCh != '\n' && yyCh != '"' ) {
279 if ( yyCh == '\\' ) {
280 yyCh = getChar();
281
282 if ( yyCh == '\n' ) {
283 yyCh = getChar();
284 } else if ( yyCh == 'x' ) {
285 QCString hex = "0";
286
287 yyCh = getChar();
288 while ( isxdigit(yyCh) ) {
289 hex += (char) yyCh;
290 yyCh = getChar();
291 }
292 sscanf( hex, "%x", &n );
293 if ( yyStringLen < sizeof(yyString) - 1 )
294 yyString[yyStringLen++] = (char) n;
295 } else if ( yyCh >= '0' && yyCh < '8' ) {
296 QCString oct = "";
297
298 do {
299 oct += (char) yyCh;
300 yyCh = getChar();
301 } while ( yyCh >= '0' && yyCh < '8' );
302 sscanf( oct, "%o", &n );
303 if ( yyStringLen < sizeof(yyString) - 1 )
304 yyString[yyStringLen++] = (char) n;
305 } else {
306 const char *p = strchr( tab, yyCh );
307 if ( yyStringLen < sizeof(yyString) - 1 )
308 yyString[yyStringLen++] = ( p == 0 ) ?
309 (char) yyCh : backTab[p - tab];
310 yyCh = getChar();
311 }
312 } else {
313 if ( yyStringLen < sizeof(yyString) - 1 )
314 yyString[yyStringLen++] = (char) yyCh;
315 yyCh = getChar();
316 }
317 }
318 yyString[yyStringLen] = '\0';
319
320 if ( yyCh != '"' )
321 qWarning( "%s:%d: Unterminated C++ string",
322 (const char *) yyFileName, yyLineNo );
323
324 if ( yyCh == EOF ) {
325 return Tok_Eof;
326 } else {
327 yyCh = getChar();
328 return Tok_String;
329 }
330 break;
331 case '-':
332 yyCh = getChar();
333 if ( yyCh == '>' ) {
334 yyCh = getChar();
335 return Tok_Arrow;
336 }
337 break;
338 case ':':
339 yyCh = getChar();
340 if ( yyCh == ':' ) {
341 yyCh = getChar();
342 return Tok_Gulbrandsen;
343 }
344 return Tok_Colon;
345 case '\'':
346 yyCh = getChar();
347 if ( yyCh == '\\' )
348 yyCh = getChar();
349
350 do {
351 yyCh = getChar();
352 } while ( yyCh != EOF && yyCh != '\'' );
353 yyCh = getChar();
354 break;
355 case '{':
356 yyBraceDepth++;
357 yyCh = getChar();
358 return Tok_LeftBrace;
359 case '}':
360 yyBraceDepth--;
361 yyCh = getChar();
362 return Tok_RightBrace;
363 case '(':
364 yyParenDepth++;
365 yyCh = getChar();
366 return Tok_LeftParen;
367 case ')':
368 yyParenDepth--;
369 yyCh = getChar();
370 return Tok_RightParen;
371 case ',':
372 yyCh = getChar();
373 return Tok_Comma;
374 case ';':
375 yyCh = getChar();
376 return Tok_Semicolon;
377 default:
378 yyCh = getChar();
379 }
380 }
381 }
382 return Tok_Eof;
383}
384
385/*
386 The second part of this source file is the parser. It accomplishes
387 a very easy task: It finds all strings inside a tr() or translate()
388 call, and possibly finds out the context of the call. It supports
389 three cases: (1) the context is specified, as in
390 FunnyDialog::tr("Hello") or translate("FunnyDialog", "Hello");
391 (2) the call appears within an inlined function; (3) the call
392 appears within a function defined outside the class definition.
393*/
394
395static int yyTok;
396
397static bool match( int t )
398{
399 bool matches = ( yyTok == t );
400 if ( matches )
401 yyTok = getToken();
402 return matches;
403}
404
405static bool matchString( QCString *s )
406{
407 bool matches = ( yyTok == Tok_String );
408 *s = "";
409 while ( yyTok == Tok_String ) {
410 *s += yyString;
411 yyTok = getToken();
412 }
413 return matches;
414}
415
416static bool matchEncoding( bool *utf8 )
417{
418 if ( yyTok == Tok_Ident ) {
419 if ( strcmp(yyIdent, "QApplication") == 0 ) {
420 yyTok = getToken();
421 if ( yyTok == Tok_Gulbrandsen )
422 yyTok = getToken();
423 }
424 *utf8 = QString( yyIdent ).endsWith( QString("UTF8") );
425 yyTok = getToken();
426 return TRUE;
427 } else {
428 return FALSE;
429 }
430}
431
432static void parse( MetaTranslator *tor, const char *initialContext,
433 const char *defaultContext )
434{
435 QMap<QCString, QCString> qualifiedContexts;
436 QStringList namespaces;
437 QCString context;
438 QCString text;
439 QCString com;
440 QCString functionContext = initialContext;
441 QCString prefix;
442 bool utf8 = FALSE;
443 bool missing_Q_OBJECT = FALSE;
444
445 yyTok = getToken();
446 while ( yyTok != Tok_Eof ) {
447 switch ( yyTok ) {
448 case Tok_class:
449 /*
450 Partial support for inlined functions.
451 */
452 yyTok = getToken();
453 if ( yyBraceDepth == (int) namespaces.count() &&
454 yyParenDepth == 0 ) {
455 do {
456 /*
457 This code should execute only once, but we play
458 safe with impure definitions such as
459 'class Q_EXPORT QMessageBox', in which case
460 'QMessageBox' is the class name, not 'Q_EXPORT'.
461 */
462 functionContext = yyIdent;
463 yyTok = getToken();
464 } while ( yyTok == Tok_Ident );
465
466 while ( yyTok == Tok_Gulbrandsen ) {
467 yyTok = getToken();
468 functionContext += "::";
469 functionContext += yyIdent;
470 yyTok = getToken();
471 }
472
473 if ( yyTok == Tok_Colon ) {
474 missing_Q_OBJECT = TRUE;
475 } else {
476 functionContext = defaultContext;
477 }
478 }
479 break;
480 case Tok_namespace:
481 yyTok = getToken();
482 if ( yyTok == Tok_Ident ) {
483 QCString ns = yyIdent;
484 yyTok = getToken();
485 if ( yyTok == Tok_LeftBrace &&
486 yyBraceDepth == (int) namespaces.count() + 1 )
487 namespaces.append( QString(ns) );
488 }
489 break;
490 case Tok_tr:
491 case Tok_trUtf8:
492 utf8 = ( yyTok == Tok_trUtf8 );
493 yyTok = getToken();
494 if ( match(Tok_LeftParen) && matchString(&text) ) {
495 com = "";
496 if ( match(Tok_RightParen) || (match(Tok_Comma) &&
497 matchString(&com) && match(Tok_RightParen)) ) {
498 if ( prefix.isNull() ) {
499 context = functionContext;
500 if ( !namespaces.isEmpty() )
501 context.prepend( (namespaces.join(QString("::")) +
502 QString("::")).latin1() );
503 } else {
504 context = prefix;
505 }
506 prefix = (const char *) 0;
507
508 if ( qualifiedContexts.contains(context) )
509 context = qualifiedContexts[context];
510 tor->insert( MetaTranslatorMessage(context, text, com,
511 QString::null, utf8) );
512
513 if ( lacks_Q_OBJECT.contains(context) ) {
514 qWarning( "%s:%d: Class '%s' lacks Q_OBJECT macro",
515 (const char *) yyFileName, yyLineNo,
516 (const char *) context );
517 lacks_Q_OBJECT.remove( context );
518 } else {
519 needs_Q_OBJECT.insert( context, 0 );
520 }
521 }
522 }
523 break;
524 case Tok_translate:
525 utf8 = FALSE;
526 yyTok = getToken();
527 if ( match(Tok_LeftParen) &&
528 matchString(&context) &&
529 match(Tok_Comma) &&
530 matchString(&text) ) {
531 com = "";
532 if ( match(Tok_RightParen) ||
533 (match(Tok_Comma) &&
534 matchString(&com) &&
535 (match(Tok_RightParen) ||
536 match(Tok_Comma) &&
537 matchEncoding(&utf8) &&
538 match(Tok_RightParen))) )
539 tor->insert( MetaTranslatorMessage(context, text, com,
540 QString::null, utf8) );
541 }
542 break;
543 case Tok_Q_OBJECT:
544 missing_Q_OBJECT = FALSE;
545 yyTok = getToken();
546 break;
547 case Tok_Ident:
548 if ( !prefix.isNull() )
549 prefix += "::";
550 prefix += yyIdent;
551 yyTok = getToken();
552 if ( yyTok != Tok_Gulbrandsen )
553 prefix = (const char *) 0;
554 break;
555 case Tok_Comment:
556 com = yyComment;
557 com = com.simplifyWhiteSpace();
558 if ( com.left(sizeof(MagicComment) - 1) == MagicComment ) {
559 com.remove( 0, sizeof(MagicComment) - 1 );
560 int k = com.find( ' ' );
561 if ( k == -1 ) {
562 context = com;
563 } else {
564 context = com.left( k );
565 com.remove( 0, k + 1 );
566 tor->insert( MetaTranslatorMessage(context, "", com,
567 QString::null, FALSE) );
568 }
569
570 /*
571 Provide a backdoor for people using "using
572 namespace". See the manual for details.
573 */
574 k = 0;
575 while ( (k = context.find("::", k)) != -1 ) {
576 qualifiedContexts.insert( context.mid(k + 2), context );
577 k++;
578 }
579 }
580 yyTok = getToken();
581 break;
582 case Tok_Arrow:
583 yyTok = getToken();
584 if ( yyTok == Tok_tr || yyTok == Tok_trUtf8 )
585 qWarning( "%s:%d: Cannot invoke tr() like this",
586 (const char *) yyFileName, yyLineNo );
587 break;
588 case Tok_Gulbrandsen:
589 // at top level?
590 if ( yyBraceDepth == (int) namespaces.count() && yyParenDepth == 0 )
591 functionContext = prefix;
592 yyTok = getToken();
593 break;
594 case Tok_RightBrace:
595 case Tok_Semicolon:
596 if ( yyBraceDepth >= 0 &&
597 yyBraceDepth + 1 == (int) namespaces.count() )
598 namespaces.remove( namespaces.fromLast() );
599 if ( yyBraceDepth == (int) namespaces.count() ) {
600 if ( missing_Q_OBJECT ) {
601 if ( needs_Q_OBJECT.contains(functionContext) ) {
602 qWarning( "%s:%d: Class '%s' lacks Q_OBJECT macro",
603 (const char *) yyFileName, yyLineNo,
604 (const char *) functionContext );
605 } else {
606 lacks_Q_OBJECT.insert( functionContext, 0 );
607 }
608 }
609 functionContext = defaultContext;
610 missing_Q_OBJECT = FALSE;
611 }
612 yyTok = getToken();
613 break;
614 default:
615 yyTok = getToken();
616 }
617 }
618
619 if ( yyBraceDepth != 0 )
620 fprintf( stderr,
621 "%s: Unbalanced braces in C++ code (or abuse of the C++"
622 " preprocessor)\n",
623 (const char *) yyFileName );
624 if ( yyParenDepth != 0 )
625 fprintf( stderr,
626 "%s: Unbalanced parentheses in C++ code (or abuse of the C++"
627 " preprocessor)\n",
628 (const char *) yyFileName );
629}
630
631void fetchtr_cpp( const char *fileName, MetaTranslator *tor,
632 const char *defaultContext, bool mustExist )
633{
634 yyInFile = fopen( fileName, "r" );
635 if ( yyInFile == 0 ) {
636 if ( mustExist )
637 fprintf( stderr,
638 "lupdate error: Cannot open C++ source file '%s': %s\n",
639 fileName, strerror(errno) );
640 return;
641 }
642
643 startTokenizer( fileName, getCharFromFile );
644 parse( tor, 0, defaultContext );
645 fclose( yyInFile );
646}
647
648/*
649 In addition to C++, we support Qt Designer UI files.
650*/
651
652/*
653 Fetches tr() calls in C++ code in UI files (inside "<function>"
654 tag). This mechanism is obsolete.
655*/
656void fetchtr_inlined_cpp( const char *fileName, const QString& in,
657 MetaTranslator *tor, const char *context )
658{
659 yyInStr = in;
660 startTokenizer( fileName, getCharFromString );
661 parse( tor, context, 0 );
662 yyInStr = QString::null;
663}
664
665class UiHandler : public QXmlDefaultHandler
666{
667public:
668 UiHandler( MetaTranslator *translator, const char *fileName )
669 : tor( translator ), fname( fileName ), comment( "" ) { }
670
671 virtual bool startElement( const QString& namespaceURI,
672 const QString& localName, const QString& qName,
673 const QXmlAttributes& atts );
674 virtual bool endElement( const QString& namespaceURI,
675 const QString& localName, const QString& qName );
676 virtual bool characters( const QString& ch );
677 virtual bool fatalError( const QXmlParseException& exception );
678
679private:
680 void flush();
681
682 MetaTranslator *tor;
683 QCString fname;
684 QString context;
685 QString source;
686 QString comment;
687
688 QString accum;
689};
690
691bool UiHandler::startElement( const QString& /* namespaceURI */,
692 const QString& /* localName */,
693 const QString& qName,
694 const QXmlAttributes& atts )
695{
696 if ( qName == QString("item") ) {
697 flush();
698 if ( !atts.value(QString("text")).isEmpty() )
699 source = atts.value( QString("text") );
700 } else if ( qName == QString("string") ) {
701 flush();
702 }
703 accum.truncate( 0 );
704 return TRUE;
705}
706
707bool UiHandler::endElement( const QString& /* namespaceURI */,
708 const QString& /* localName */,
709 const QString& qName )
710{
711 accum.replace( QRegExp(QString("\r\n")), "\n" );
712
713 if ( qName == QString("class") ) {
714 if ( context.isEmpty() )
715 context = accum;
716 } else if ( qName == QString("string") ) {
717 source = accum;
718 } else if ( qName == QString("comment") ) {
719 comment = accum;
720 flush();
721 } else if ( qName == QString("function") ) {
722 fetchtr_inlined_cpp( (const char *) fname, accum, tor,
723 context.latin1() );
724 } else {
725 flush();
726 }
727 return TRUE;
728}
729
730bool UiHandler::characters( const QString& ch )
731{
732 accum += ch;
733 return TRUE;
734}
735
736bool UiHandler::fatalError( const QXmlParseException& exception )
737{
738 QString msg;
739 msg.sprintf( "Parse error at line %d, column %d (%s).",
740 exception.lineNumber(), exception.columnNumber(),
741 exception.message().latin1() );
742 fprintf( stderr, "XML error: %s\n", msg.latin1() );
743 return FALSE;
744}
745
746void UiHandler::flush()
747{
748 if ( !context.isEmpty() && !source.isEmpty() )
749 tor->insert( MetaTranslatorMessage(context.utf8(), source.utf8(),
750 comment.utf8(), QString::null,
751 TRUE) );
752 source.truncate( 0 );
753 comment.truncate( 0 );
754}
755
756void fetchtr_ui( const char *fileName, MetaTranslator *tor,
757 const char * /* defaultContext */, bool mustExist )
758{
759 QFile f( fileName );
760 if ( !f.open(IO_ReadOnly) ) {
761 if ( mustExist )
762 fprintf( stderr, "lupdate error: cannot open UI file '%s': %s\n",
763 fileName, strerror(errno) );
764 return;
765 }
766
767 QTextStream t( &f );
768 QXmlInputSource in( t );
769 QXmlSimpleReader reader;
770 reader.setFeature( "http://xml.org/sax/features/namespaces", FALSE );
771 reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", TRUE );
772 reader.setFeature( "http://trolltech.com/xml/features/report-whitespace"
773 "-only-CharData", FALSE );
774 QXmlDefaultHandler *hand = new UiHandler( tor, fileName );
775 reader.setContentHandler( hand );
776 reader.setErrorHandler( hand );
777
778 if ( !reader.parse(in) )
779 fprintf( stderr, "%s: Parse error in UI file\n", fileName );
780 reader.setContentHandler( 0 );
781 reader.setErrorHandler( 0 );
782 delete hand;
783 f.close();
784}
diff --git a/development/translation/opie-lupdate/lupdate b/development/translation/opie-lupdate/lupdate
new file mode 100755
index 0000000..0e1604d
--- a/dev/null
+++ b/development/translation/opie-lupdate/lupdate
Binary files differ
diff --git a/development/translation/opie-lupdate/main.cpp b/development/translation/opie-lupdate/main.cpp
new file mode 100644
index 0000000..ce65e7a
--- a/dev/null
+++ b/development/translation/opie-lupdate/main.cpp
@@ -0,0 +1,215 @@
1/**********************************************************************
2** Copyright (C) 2000-2002 Trolltech AS. All rights reserved.
3** Copyright (C) 2003 zecke
4**
5** This file is part of Qt Linguist.
6**
7** This file may be distributed and/or modified under the terms of the
8** GNU General Public License version 2 as published by the Free Software
9** Foundation and appearing in the file LICENSE.GPL included in the
10** packaging of this file.
11**
12** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
13** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
14**
15** See http://www.trolltech.com/gpl/ for GPL licensing information.
16**
17** Contact info@trolltech.com if any conditions of this licensing are
18** not clear to you.
19**
20**********************************************************************/
21
22#include <metatranslator.h>
23#include <proparser.h>
24#include <opie.h>
25
26#include <qfile.h>
27#include <qfileinfo.h>
28#include <qstring.h>
29#include <qstringlist.h>
30#include <qtextstream.h>
31
32#include <errno.h>
33#include <string.h>
34
35// defined in fetchtr.cpp
36extern void fetchtr_cpp( const char *fileName, MetaTranslator *tor,
37 const char *defaultContext, bool mustExist );
38extern void fetchtr_ui( const char *fileName, MetaTranslator *tor,
39 const char *defaultContext, bool mustExist );
40
41// defined in merge.cpp
42extern void merge( MetaTranslator *tor, const MetaTranslator *virginTor,
43 bool verbose );
44
45typedef QValueList<MetaTranslatorMessage> TML;
46
47static const char* LUPDATE_VERSION = "0.1";
48
49static void printUsage()
50{
51 fprintf( stderr, "Usage:\n"
52 " opie-lupdate [options] project-file\n"
53 " opie-lupdate [options] source-files -ts ts-files\n"
54 "Options:\n"
55 " -opie The OPIE base dir if not supplied $OPIEDIR will be taken\n"
56 " -help Display this information and exit\n"
57 " -noobsolete\n"
58 " Drop all obsolete strings\n"
59 " -verbose\n"
60 " Explain what is being done\n"
61 " -version\n"
62 " Display the version of lupdate and exit\n" );
63}
64
65static void updateTsFiles( const MetaTranslator& fetchedTor,
66 const QString& opiedir,
67 const QStringList& languages,
68 const QString& basename,
69 const QString& codec,
70 bool noObsolete, bool verbose )
71{
72 QStringList::ConstIterator it = languages.begin();
73 for ( ; it != languages.end(); ++it ) {
74 QString fileName = opiedir + "/i18n/" + (*it) + "/" + basename;
75 MetaTranslator tor;
76 tor.load( fileName );
77 if ( !codec.isEmpty() )
78 tor.setCodec( codec );
79 if ( verbose )
80 fprintf( stderr, "Updating '%s'...\n", fileName.latin1() );
81 merge( &tor, &fetchedTor, verbose );
82 if ( noObsolete )
83 tor.stripObsoleteMessages();
84 tor.stripEmptyContexts();
85 if ( !tor.save(fileName) )
86 fprintf( stderr, "lupdate error: Cannot save '%s': %s\n",
87 fileName.latin1(), strerror(errno) );
88 }
89}
90
91int main( int argc, char **argv )
92{
93 QString defaultContext = "@default";
94 MetaTranslator fetchedTor;
95 QCString codec;
96 QStringList tsFileNames;
97 QString opiedir;
98 QString translationBase;
99 QString target;
100
101 bool verbose = FALSE;
102 bool noObsolete = FALSE;
103 bool metSomething = FALSE;
104 bool isLib = FALSE;
105 int numFiles = 0;
106
107 int i;
108
109 QStringList languageList = OPIE::self()->languageList(opiedir);
110
111 for ( i = 1; i < argc; i++ ) {
112 if ( qstrcmp(argv[i], "-help") == 0 ) {
113 printUsage();
114 return 0;
115 } else if ( qstrcmp(argv[i], "-noobsolete") == 0 ) {
116 noObsolete = TRUE;
117 continue;
118 } else if ( qstrcmp(argv[i], "-verbose") == 0 ) {
119 verbose = TRUE;
120 continue;
121 } else if ( qstrcmp(argv[i], "-version") == 0 ) {
122 fprintf( stderr, "lupdate version %s\n", LUPDATE_VERSION );
123 return 0;
124 } else if ( qstrcmp(argv[i], "-opie") == 0 ) {
125 if( i+1 < argc ) {
126 opiedir = argv[i+1];
127 languageList = OPIE::self()->languageList(opiedir);
128 }
129 i++; // UGLY but we want to skip the next argument
130 continue;
131 }
132
133 numFiles++;
134
135 QString fullText;
136
137 QFile f( argv[i] );
138 if ( !f.open(IO_ReadOnly) ) {
139 fprintf( stderr, "lupdate error: Cannot open file '%s': %s\n",
140 argv[i], strerror(errno) );
141 return 1;
142 }
143
144 QTextStream t( &f );
145 fullText = t.read();
146 f.close();
147
148 fetchedTor = MetaTranslator();
149 codec.truncate( 0 );
150 tsFileNames.clear();
151 isLib = FALSE;
152
153 QMap<QString, QString> tagMap = proFileTagMap( fullText );
154 QMap<QString, QString>::Iterator it;
155
156 for ( it = tagMap.begin(); it != tagMap.end(); ++it ) {
157 QStringList toks = QStringList::split( ' ', it.data() );
158 QStringList::Iterator t;
159
160 for ( t = toks.begin(); t != toks.end(); ++t ) {
161 if ( it.key() == "HEADERS" || it.key() == "SOURCES" ) {
162 fetchtr_cpp( *t, &fetchedTor, defaultContext, TRUE );
163 metSomething = TRUE;
164 } else if ( it.key() == "INTERFACES" ||
165 it.key() == "FORMS" ) {
166 fetchtr_ui( *t, &fetchedTor, defaultContext, TRUE );
167 fetchtr_cpp( *t + ".h", &fetchedTor, defaultContext,
168 FALSE );
169 metSomething = TRUE;
170 } else if ( it.key() == "TRANSLATIONS" ) {
171 // we do not care for that attribute anymore
172 //tsFileNames.append( *t );
173 metSomething = TRUE;
174 } else if ( it.key() == "CODEC" ) {
175 codec = (*t).latin1();
176 } else if ( it.key() == "TARGET" ) {
177 target = *t;
178 metSomething = TRUE;
179 } else if ( it.key() == "TEMPLATE" ) {
180 if ( (*t).stripWhiteSpace().lower() == "lib" )
181 isLib = true;
182 }
183 }
184 }
185 /**
186 * We know the $OPIEDIR or have opiedir
187 * we've a list of languages (de,en,gb,foo,bar)
188 * we've got the TARGET and we no it's the lib
189 * so let's do that
190 * $OPIEDIR/language[i]/ifLibAppendLib$TARGET.ts
191 */
192 qWarning("TARGET %s IsLib:%d", target.latin1(), isLib );
193 qWarning("LANGS %s", languageList.join(";").latin1() );
194 qWarning("OPIEDIR %s", OPIE::self()->opieDir(opiedir).latin1() );
195 if (isLib )
196 target.prepend("lib");
197 target += ".ts";
198 updateTsFiles( fetchedTor, OPIE::self()->opieDir(opiedir),
199 languageList, target, codec, noObsolete, verbose );
200
201 if ( !metSomething ) {
202 fprintf( stderr,
203 "lupdate warning: File '%s' does not look like a"
204 " project file\n",
205 argv[i] );
206 }
207
208 }
209
210 if ( numFiles == 0 ) {
211 printUsage();
212 return 1;
213 }
214 return 0;
215}
diff --git a/development/translation/opie-lupdate/merge.cpp b/development/translation/opie-lupdate/merge.cpp
new file mode 100644
index 0000000..a96104e
--- a/dev/null
+++ b/development/translation/opie-lupdate/merge.cpp
@@ -0,0 +1,115 @@
1/**********************************************************************
2** Copyright (C) 2000 Trolltech AS. All rights reserved.
3**
4** This file is part of Qt Linguist.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20
21#include <metatranslator.h>
22
23// defined in numberh.cpp
24extern void applyNumberHeuristic( MetaTranslator *tor, bool verbose );
25// defined in sametexth.cpp
26extern void applySameTextHeuristic( MetaTranslator *tor, bool verbose );
27
28typedef QValueList<MetaTranslatorMessage> TML;
29
30/*
31 Merges two MetaTranslator objects into the first one. The first one
32 is a set of source texts and translations for a previous version of
33 the internationalized program; the second one is a set of fresh
34 source texts newly extracted from the source code, without any
35 translation yet.
36*/
37
38void merge( MetaTranslator *tor, const MetaTranslator *virginTor, bool verbose )
39{
40 int known = 0;
41 int neww = 0;
42 int obsoleted = 0;
43 TML all = tor->messages();
44 TML::Iterator it;
45
46 /*
47 The types of all the messages from the vernacular translator
48 are updated according to the virgin translator.
49 */
50 for ( it = all.begin(); it != all.end(); ++it ) {
51 MetaTranslatorMessage::Type newType;
52 MetaTranslatorMessage m = *it;
53
54 // skip context comment
55 if ( !QCString((*it).sourceText()).isEmpty() ) {
56 if ( !virginTor->contains((*it).context(), (*it).sourceText(),
57 (*it).comment()) ) {
58 newType = MetaTranslatorMessage::Obsolete;
59 if ( m.type() != MetaTranslatorMessage::Obsolete )
60 obsoleted++;
61 } else {
62 switch ( m.type() ) {
63 case MetaTranslatorMessage::Finished:
64 newType = MetaTranslatorMessage::Finished;
65 known++;
66 break;
67 case MetaTranslatorMessage::Unfinished:
68 newType = MetaTranslatorMessage::Unfinished;
69 known++;
70 break;
71 case MetaTranslatorMessage::Obsolete:
72 newType = MetaTranslatorMessage::Unfinished;
73 neww++;
74 }
75 }
76
77 if ( newType != m.type() ) {
78 m.setType( newType );
79 tor->insert( m );
80 }
81 }
82 }
83
84 /*
85 Messages found only in the virgin translator are added to the
86 vernacular translator. Among these are all the context comments.
87 */
88 all = virginTor->messages();
89
90 for ( it = all.begin(); it != all.end(); ++it ) {
91 if ( !tor->contains((*it).context(), (*it).sourceText(),
92 (*it).comment()) ) {
93 tor->insert( *it );
94 if ( !QCString((*it).sourceText()).isEmpty() )
95 neww++;
96 }
97 }
98
99 /*
100 The same-text heuristic handles cases where a message has an
101 obsolete counterpart with a different context or comment.
102 */
103 applySameTextHeuristic( tor, verbose );
104
105 /*
106 The number heuristic handles cases where a message has an
107 obsolete counterpart with mostly numbers differing in the
108 source text.
109 */
110 applyNumberHeuristic( tor, verbose );
111
112 if ( verbose )
113 fprintf( stderr, " %d known, %d new and %d obsoleted messages\n", known,
114 neww, obsoleted );
115}
diff --git a/development/translation/opie-lupdate/numberh.cpp b/development/translation/opie-lupdate/numberh.cpp
new file mode 100644
index 0000000..f7b7bf8
--- a/dev/null
+++ b/development/translation/opie-lupdate/numberh.cpp
@@ -0,0 +1,235 @@
1/**********************************************************************
2** Copyright (C) 2000-2002 Trolltech AS. All rights reserved.
3**
4** This file is part of Qt Linguist.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20
21#include <metatranslator.h>
22
23#include <qmemarray.h>
24#include <qcstring.h>
25#include <qmap.h>
26#include <qstringlist.h>
27
28#include <ctype.h>
29
30typedef QMap<QCString, MetaTranslatorMessage> TMM;
31typedef QValueList<MetaTranslatorMessage> TML;
32
33static bool isDigitFriendly( int c )
34{
35 return ispunct( c ) || isspace( c );
36}
37
38static int numberLength( const char *s )
39{
40 int i = 0;
41
42 if ( isdigit(s[0]) ) {
43 do {
44 i++;
45 } while ( isdigit(s[i]) ||
46 (isDigitFriendly(s[i]) &&
47 (isdigit(s[i + 1]) ||
48 (isDigitFriendly(s[i + 1]) && isdigit(s[i + 2])))) );
49 }
50 return i;
51}
52
53/*
54 Returns a version of 'key' where all numbers have been replaced by zeroes. If
55 there were none, returns "".
56*/
57static QCString zeroKey( const char *key )
58{
59 QCString zeroed( strlen(key) + 1 );
60 char *z = zeroed.data();
61 int i = 0, j = 0;
62 int len;
63 bool metSomething = FALSE;
64
65 while ( key[i] != '\0' ) {
66 len = numberLength( key + i );
67 if ( len > 0 ) {
68 i += len;
69 z[j++] = '0';
70 metSomething = TRUE;
71 } else {
72 z[j++] = key[i++];
73 }
74 }
75 z[j] = '\0';
76
77 if ( metSomething )
78 return zeroed;
79 else
80 return "";
81}
82
83static QString translationAttempt( const QString& oldTranslation,
84 const char *oldSource,
85 const char *newSource )
86{
87 int p = zeroKey( oldSource ).contains( '0' );
88 int oldSourceLen = qstrlen( oldSource );
89 QString attempt;
90 QStringList oldNumbers;
91 QStringList newNumbers;
92 QMemArray<bool> met( p );
93 QMemArray<int> matchedYet( p );
94 int i, j;
95 int k = 0, ell, best;
96 int m, n;
97 int pass;
98
99 /*
100 This algorithm is hard to follow, so we'll consider an example
101 all along: oldTranslation is "XeT 3.0", oldSource is "TeX 3.0"
102 and newSource is "XeT 3.1".
103
104 First, we set up two tables: oldNumbers and newNumbers. In our
105 example, oldNumber[0] is "3.0" and newNumber[0] is "3.1".
106 */
107 for ( i = 0, j = 0; i < oldSourceLen; i++, j++ ) {
108 m = numberLength( oldSource + i );
109 n = numberLength( newSource + j );
110 if ( m > 0 ) {
111 oldNumbers.append( QCString(oldSource + i, m + 1) );
112 newNumbers.append( QCString(newSource + j, n + 1) );
113 i += m;
114 j += n;
115 met[k] = FALSE;
116 matchedYet[k] = 0;
117 k++;
118 }
119 }
120
121 /*
122 We now go over the old translation, "XeT 3.0", one letter at a
123 time, looking for numbers found in oldNumbers. Whenever such a
124 number is met, it is replaced with its newNumber equivalent. In
125 our example, the "3.0" of "XeT 3.0" becomes "3.1".
126 */
127 for ( i = 0; i < (int) oldTranslation.length(); i++ ) {
128 attempt += oldTranslation[i];
129 for ( k = 0; k < p; k++ ) {
130 if ( oldTranslation[i] == oldNumbers[k][matchedYet[k]] )
131 matchedYet[k]++;
132 else
133 matchedYet[k] = 0;
134 }
135
136 /*
137 Let's find out if the last character ended a match. We make
138 two passes over the data. In the first pass, we try to
139 match only numbers that weren't matched yet; if that fails,
140 the second pass does the trick. This is useful in some
141 suspicious cases, flagged below.
142 */
143 for ( pass = 0; pass < 2; pass++ ) {
144 best = p; // an impossible value
145 for ( k = 0; k < p; k++ ) {
146 if ( (!met[k] || pass > 0) &&
147 matchedYet[k] == (int) oldNumbers[k].length() &&
148 numberLength(oldTranslation.latin1() + (i + 1) -
149 matchedYet[k]) == matchedYet[k] ) {
150 // the longer the better
151 if ( best == p || matchedYet[k] > matchedYet[best] )
152 best = k;
153 }
154 }
155 if ( best != p ) {
156 attempt.truncate( attempt.length() - matchedYet[best] );
157 attempt += newNumbers[best];
158 met[best] = TRUE;
159 for ( k = 0; k < p; k++ )
160 matchedYet[k] = 0;
161 break;
162 }
163 }
164 }
165
166 /*
167 We flag two kinds of suspicious cases. They are identified as
168 such with comments such as "{2000?}" at the end.
169
170 Example of the first kind: old source text "TeX 3.0" translated
171 as "XeT 2.0" is flagged "TeX 2.0 {3.0?}", no matter what the
172 new text is.
173 */
174 for ( k = 0; k < p; k++ ) {
175 if ( !met[k] )
176 attempt += QString( " {" ) + newNumbers[k] + QString( "?}" );
177 }
178
179 /*
180 Example of the second kind: "1 of 1" translated as "1 af 1",
181 with new source text "1 of 2", generates "1 af 2 {1 or 2?}"
182 because it's not clear which of "1 af 2" and "2 af 1" is right.
183 */
184 for ( k = 0; k < p; k++ ) {
185 for ( ell = 0; ell < p; ell++ ) {
186 if ( k != ell && oldNumbers[k] == oldNumbers[ell] &&
187 newNumbers[k] < newNumbers[ell] )
188 attempt += QString( " {" ) + newNumbers[k] + QString( " or " ) +
189 newNumbers[ell] + QString( "?}" );
190 }
191 }
192 return attempt;
193}
194
195/*
196 Augments a MetaTranslator with translations easily derived from
197 similar existing (probably obsolete) translations.
198
199 For example, if "TeX 3.0" is translated as "XeT 3.0" and "TeX 3.1"
200 has no translation, "XeT 3.1" is added to the translator and is
201 marked Unfinished.
202*/
203void applyNumberHeuristic( MetaTranslator *tor, bool verbose )
204{
205 TMM translated, untranslated;
206 TMM::Iterator t, u;
207 TML all = tor->messages();
208 TML::Iterator it;
209 int inserted = 0;
210
211 for ( it = all.begin(); it != all.end(); ++it ) {
212 if ( (*it).type() == MetaTranslatorMessage::Unfinished ) {
213 if ( (*it).translation().isEmpty() )
214 untranslated.insert( zeroKey((*it).sourceText()), *it );
215 } else if ( !(*it).translation().isEmpty() ) {
216 translated.insert( zeroKey((*it).sourceText()), *it );
217 }
218 }
219
220 for ( u = untranslated.begin(); u != untranslated.end(); ++u ) {
221 t = translated.find( u.key() );
222 if ( t != translated.end() && !t.key().isEmpty() &&
223 qstrcmp((*t).sourceText(), (*u).sourceText()) != 0 ) {
224 MetaTranslatorMessage m( *u );
225 m.setTranslation( translationAttempt((*t).translation(),
226 (*t).sourceText(),
227 (*u).sourceText()) );
228 tor->insert( m );
229 inserted++;
230 }
231 }
232 if ( verbose && inserted != 0 )
233 fprintf( stderr, " number heuristic provided %d translation%s\n",
234 inserted, inserted == 1 ? "" : "s" );
235}
diff --git a/development/translation/opie-lupdate/opie-lupdate.pro b/development/translation/opie-lupdate/opie-lupdate.pro
new file mode 100644
index 0000000..ea51c8b
--- a/dev/null
+++ b/development/translation/opie-lupdate/opie-lupdate.pro
@@ -0,0 +1,21 @@
1 TEMPLATE= app
2 CONFIG += qt warn_on console
3 HEADERS = ../shared/metatranslator.h \
4 ../shared/proparser.h \
5 ../shared/opie.h
6
7 SOURCES = fetchtr.cpp \
8 main.cpp \
9 merge.cpp \
10 numberh.cpp \
11 sametexth.cpp \
12 ../shared/metatranslator.cpp \
13 ../shared/proparser.cpp \
14 ../shared/opie.cpp
15
16 DEFINES += QT_INTERNAL_XML
17
18 TARGET = opie-lupdate
19 INCLUDEPATH+= ../shared
20 #DESTDIR =
21
diff --git a/development/translation/opie-lupdate/sametexth.cpp b/development/translation/opie-lupdate/sametexth.cpp
new file mode 100644
index 0000000..574cfd5
--- a/dev/null
+++ b/development/translation/opie-lupdate/sametexth.cpp
@@ -0,0 +1,84 @@
1/**********************************************************************
2** Copyright (C) 2000-2002 Trolltech AS. All rights reserved.
3**
4** This file is part of Qt Linguist.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20
21#include <metatranslator.h>
22
23#include <qcstring.h>
24#include <qmap.h>
25
26typedef QMap<QCString, MetaTranslatorMessage> TMM;
27typedef QValueList<MetaTranslatorMessage> TML;
28
29/*
30 Augments a MetaTranslator with trivially derived translations.
31
32 For example, if "Enabled:" is consistendly translated as "Eingeschaltet:" no
33 matter the context or the comment, "Eingeschaltet:" is added as the
34 translation of any untranslated "Enabled:" text and is marked Unfinished.
35*/
36
37void applySameTextHeuristic( MetaTranslator *tor, bool verbose )
38{
39 TMM translated;
40 TMM avoid;
41 TMM::Iterator t;
42 TML untranslated;
43 TML::Iterator u;
44 TML all = tor->messages();
45 TML::Iterator it;
46 int inserted = 0;
47
48 for ( it = all.begin(); it != all.end(); ++it ) {
49 if ( (*it).type() == MetaTranslatorMessage::Unfinished ) {
50 if ( (*it).translation().isEmpty() )
51 untranslated.append( *it );
52 } else {
53 QCString key = (*it).sourceText();
54 t = translated.find( key );
55 if ( t != translated.end() ) {
56 /*
57 The same source text is translated at least two
58 different ways. Do nothing then.
59 */
60 if ( (*t).translation() != (*it).translation() ) {
61 translated.remove( key );
62 avoid.insert( key, *it );
63 }
64 } else if ( !avoid.contains(key) &&
65 !(*it).translation().isEmpty() ) {
66 translated.insert( key, *it );
67 }
68 }
69 }
70
71 for ( u = untranslated.begin(); u != untranslated.end(); ++u ) {
72 QCString key = (*u).sourceText();
73 t = translated.find( key );
74 if ( t != translated.end() ) {
75 MetaTranslatorMessage m( *u );
76 m.setTranslation( (*t).translation() );
77 tor->insert( m );
78 inserted++;
79 }
80 }
81 if ( verbose && inserted != 0 )
82 fprintf( stderr, " same-text heuristic provided %d translation%s\n",
83 inserted, inserted == 1 ? "" : "s" );
84}
diff --git a/development/translation/shared/metatranslator.cpp b/development/translation/shared/metatranslator.cpp
new file mode 100644
index 0000000..a01e1eb
--- a/dev/null
+++ b/development/translation/shared/metatranslator.cpp
@@ -0,0 +1,586 @@
1/**********************************************************************
2** Copyright (C) 2000-2002 Trolltech AS. All rights reserved.
3**
4** This file is part of Qt Linguist.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20
21#include "metatranslator.h"
22
23#include <qapplication.h>
24#include <qcstring.h>
25#include <qfile.h>
26#include <qmessagebox.h>
27#include <qtextcodec.h>
28#include <qtextstream.h>
29#include <qxml.h>
30
31static bool encodingIsUtf8( const QXmlAttributes& atts )
32{
33 for ( int i = 0; i < atts.length(); i++ ) {
34 // utf8="true" is a pre-3.0 syntax
35 if ( atts.qName(i) == QString("utf8") ) {
36 return ( atts.value(i) == QString("true") );
37 } else if ( atts.qName(i) == QString("encoding") ) {
38 return ( atts.value(i) == QString("UTF-8") );
39 }
40 }
41 return FALSE;
42}
43
44class TsHandler : public QXmlDefaultHandler
45{
46public:
47 TsHandler( MetaTranslator *translator )
48 : tor( translator ), type( MetaTranslatorMessage::Finished ),
49 inMessage( FALSE ), ferrorCount( 0 ), contextIsUtf8( FALSE ),
50 messageIsUtf8( FALSE ) { }
51
52 virtual bool startElement( const QString& namespaceURI,
53 const QString& localName, const QString& qName,
54 const QXmlAttributes& atts );
55 virtual bool endElement( const QString& namespaceURI,
56 const QString& localName, const QString& qName );
57 virtual bool characters( const QString& ch );
58 virtual bool fatalError( const QXmlParseException& exception );
59
60private:
61 MetaTranslator *tor;
62 MetaTranslatorMessage::Type type;
63 bool inMessage;
64 QString context;
65 QString source;
66 QString comment;
67 QString translation;
68
69 QString accum;
70 int ferrorCount;
71 bool contextIsUtf8;
72 bool messageIsUtf8;
73};
74
75bool TsHandler::startElement( const QString& /* namespaceURI */,
76 const QString& /* localName */,
77 const QString& qName,
78 const QXmlAttributes& atts )
79{
80 if ( qName == QString("byte") ) {
81 for ( int i = 0; i < atts.length(); i++ ) {
82 if ( atts.qName(i) == QString("value") ) {
83 QString value = atts.value( i );
84 int base = 10;
85 if ( value.startsWith("x") ) {
86 base = 16;
87 value = value.mid( 1 );
88 }
89 int n = value.toUInt( 0, base );
90 if ( n != 0 )
91 accum += QChar( n );
92 }
93 }
94 } else {
95 if ( qName == QString("context") ) {
96 context.truncate( 0 );
97 source.truncate( 0 );
98 comment.truncate( 0 );
99 translation.truncate( 0 );
100 contextIsUtf8 = encodingIsUtf8( atts );
101 } else if ( qName == QString("message") ) {
102 inMessage = TRUE;
103 type = MetaTranslatorMessage::Finished;
104 source.truncate( 0 );
105 comment.truncate( 0 );
106 translation.truncate( 0 );
107 messageIsUtf8 = encodingIsUtf8( atts );
108 } else if ( qName == QString("translation") ) {
109 for ( int i = 0; i < atts.length(); i++ ) {
110 if ( atts.qName(i) == QString("type") ) {
111 if ( atts.value(i) == QString("unfinished") )
112 type = MetaTranslatorMessage::Unfinished;
113 else if ( atts.value(i) == QString("obsolete") )
114 type = MetaTranslatorMessage::Obsolete;
115 else
116 type = MetaTranslatorMessage::Finished;
117 }
118 }
119 }
120 accum.truncate( 0 );
121 }
122 return TRUE;
123}
124
125bool TsHandler::endElement( const QString& /* namespaceURI */,
126 const QString& /* localName */,
127 const QString& qName )
128{
129 if ( qName == QString("codec") || qName == QString("defaultcodec") ) {
130 // "codec" is a pre-3.0 syntax
131 tor->setCodec( accum );
132 } else if ( qName == QString("name") ) {
133 context = accum;
134 } else if ( qName == QString("source") ) {
135 source = accum;
136 } else if ( qName == QString("comment") ) {
137 if ( inMessage ) {
138 comment = accum;
139 } else {
140 if ( contextIsUtf8 )
141 tor->insert( MetaTranslatorMessage(context.utf8(), "",
142 accum.utf8(), QString::null, TRUE,
143 MetaTranslatorMessage::Unfinished) );
144 else
145 tor->insert( MetaTranslatorMessage(context.ascii(), "",
146 accum.ascii(), QString::null, FALSE,
147 MetaTranslatorMessage::Unfinished) );
148 }
149 } else if ( qName == QString("translation") ) {
150 translation = accum;
151 } else if ( qName == QString("message") ) {
152 if ( messageIsUtf8 )
153 tor->insert( MetaTranslatorMessage(context.utf8(), source.utf8(),
154 comment.utf8(), translation,
155 TRUE, type) );
156 else
157 tor->insert( MetaTranslatorMessage(context.ascii(), source.ascii(),
158 comment.ascii(), translation,
159 FALSE, type) );
160 inMessage = FALSE;
161 }
162 return TRUE;
163}
164
165bool TsHandler::characters( const QString& ch )
166{
167 QString t = ch;
168 t.replace( "\r", "" );
169 accum += t;
170 return TRUE;
171}
172
173bool TsHandler::fatalError( const QXmlParseException& exception )
174{
175 if ( ferrorCount++ == 0 ) {
176 QString msg;
177 msg.sprintf( "Parse error at line %d, column %d (%s).",
178 exception.lineNumber(), exception.columnNumber(),
179 exception.message().latin1() );
180 if ( qApp == 0 )
181 fprintf( stderr, "XML error: %s\n", msg.latin1() );
182 else
183 QMessageBox::information( qApp->mainWidget(),
184 QObject::tr("Qt Linguist"), msg );
185 }
186 return FALSE;
187}
188
189static QString numericEntity( int ch )
190{
191 return QString( ch <= 0x20 ? "<byte value=\"x%1\"/>" : "&#x%1;" )
192 .arg( ch, 0, 16 );
193}
194
195static QString protect( const QCString& str )
196{
197 QString result;
198 int len = (int) str.length();
199 for ( int k = 0; k < len; k++ ) {
200 switch( str[k] ) {
201 case '\"':
202 result += QString( "&quot;" );
203 break;
204 case '&':
205 result += QString( "&amp;" );
206 break;
207 case '>':
208 result += QString( "&gt;" );
209 break;
210 case '<':
211 result += QString( "&lt;" );
212 break;
213 case '\'':
214 result += QString( "&apos;" );
215 break;
216 default:
217 if ( (uchar) str[k] < 0x20 && str[k] != '\n' )
218 result += numericEntity( (uchar) str[k] );
219 else
220 result += str[k];
221 }
222 }
223 return result;
224}
225
226static QString evilBytes( const QCString& str, bool utf8 )
227{
228 if ( utf8 ) {
229 return protect( str );
230 } else {
231 QString result;
232 QCString t = protect( str ).latin1();
233 int len = (int) t.length();
234 for ( int k = 0; k < len; k++ ) {
235 if ( (uchar) t[k] >= 0x7f )
236 result += numericEntity( (uchar) t[k] );
237 else
238 result += QChar( t[k] );
239 }
240 return result;
241 }
242}
243
244MetaTranslatorMessage::MetaTranslatorMessage()
245 : utfeight( FALSE ), ty( Unfinished )
246{
247}
248
249MetaTranslatorMessage::MetaTranslatorMessage( const char *context,
250 const char *sourceText,
251 const char *comment,
252 const QString& translation,
253 bool utf8, Type type )
254 : QTranslatorMessage( context, sourceText, comment, translation ),
255 utfeight( FALSE ), ty( type )
256{
257 /*
258 Don't use UTF-8 if it makes no difference. UTF-8 should be
259 reserved for the real problematic case: non-ASCII (possibly
260 non-Latin-1) characters in .ui files.
261 */
262 if ( utf8 ) {
263 if ( sourceText != 0 ) {
264 int i = 0;
265 while ( sourceText[i] != '\0' ) {
266 if ( (uchar) sourceText[i] >= 0x80 ) {
267 utfeight = TRUE;
268 break;
269 }
270 i++;
271 }
272 }
273 if ( !utfeight && comment != 0 ) {
274 int i = 0;
275 while ( comment[i] != '\0' ) {
276 if ( (uchar) comment[i] >= 0x80 ) {
277 utfeight = TRUE;
278 break;
279 }
280 i++;
281 }
282 }
283 }
284}
285
286MetaTranslatorMessage::MetaTranslatorMessage( const MetaTranslatorMessage& m )
287 : QTranslatorMessage( m ), utfeight( m.utfeight ), ty( m.ty )
288{
289}
290
291MetaTranslatorMessage& MetaTranslatorMessage::operator=(
292 const MetaTranslatorMessage& m )
293{
294 QTranslatorMessage::operator=( m );
295 utfeight = m.utfeight;
296 ty = m.ty;
297 return *this;
298}
299
300bool MetaTranslatorMessage::operator==( const MetaTranslatorMessage& m ) const
301{
302 return qstrcmp( context(), m.context() ) == 0 &&
303 qstrcmp( sourceText(), m.sourceText() ) == 0 &&
304 qstrcmp( comment(), m.comment() ) == 0;
305}
306
307bool MetaTranslatorMessage::operator<( const MetaTranslatorMessage& m ) const
308{
309 int delta = qstrcmp( context(), m.context() );
310 if ( delta == 0 )
311 delta = qstrcmp( sourceText(), m.sourceText() );
312 if ( delta == 0 )
313 delta = qstrcmp( comment(), m.comment() );
314 return delta < 0;
315}
316
317MetaTranslator::MetaTranslator()
318 : codecName( "ISO-8859-1" ), codec( 0 )
319{
320}
321
322MetaTranslator::MetaTranslator( const MetaTranslator& tor )
323 : mm( tor.mm ), codecName( tor.codecName ), codec( tor.codec )
324{
325
326}
327
328MetaTranslator& MetaTranslator::operator=( const MetaTranslator& tor )
329{
330 mm = tor.mm;
331 codecName = tor.codecName;
332 codec = tor.codec;
333 return *this;
334}
335
336bool MetaTranslator::load( const QString& filename )
337{
338 mm.clear();
339
340 QFile f( filename );
341 if ( !f.open(IO_ReadOnly) )
342 return FALSE;
343
344 QTextStream t( &f );
345 QXmlInputSource in( t );
346 QXmlSimpleReader reader;
347 // don't click on these!
348 reader.setFeature( "http://xml.org/sax/features/namespaces", FALSE );
349 reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", TRUE );
350 reader.setFeature( "http://trolltech.com/xml/features/report-whitespace"
351 "-only-CharData", FALSE );
352 QXmlDefaultHandler *hand = new TsHandler( this );
353 reader.setContentHandler( hand );
354 reader.setErrorHandler( hand );
355
356 bool ok = reader.parse( in );
357 reader.setContentHandler( 0 );
358 reader.setErrorHandler( 0 );
359 delete hand;
360 f.close();
361 if ( !ok )
362 mm.clear();
363 return ok;
364}
365
366bool MetaTranslator::save( const QString& filename ) const
367{
368 QFile f( filename );
369 if ( !f.open(IO_WriteOnly) )
370 return FALSE;
371
372 QTextStream t( &f );
373 t.setCodec( QTextCodec::codecForName("ISO-8859-1") );
374
375 t << "<!DOCTYPE TS><TS>\n";
376 if ( codecName != "ISO-8859-1" )
377 t << "<defaultcodec>" << codecName << "</defaultcodec>\n";
378 TMM::ConstIterator m = mm.begin();
379 while ( m != mm.end() ) {
380 TMMInv inv;
381 TMMInv::Iterator i;
382 bool contextIsUtf8 = m.key().utf8();
383 QCString context = m.key().context();
384 QCString comment = "";
385
386 do {
387 if ( QCString(m.key().sourceText()).isEmpty() ) {
388 if ( m.key().type() != MetaTranslatorMessage::Obsolete ) {
389 contextIsUtf8 = m.key().utf8();
390 comment = QCString( m.key().comment() );
391 }
392 } else {
393 inv.insert( *m, m.key() );
394 }
395 } while ( ++m != mm.end() && QCString(m.key().context()) == context );
396
397 t << "<context";
398 if ( contextIsUtf8 )
399 t << " encoding=\"UTF-8\"";
400 t << ">\n";
401 t << " <name>" << evilBytes( context, contextIsUtf8 )
402 << "</name>\n";
403 if ( !comment.isEmpty() )
404 t << " <comment>" << evilBytes( comment, contextIsUtf8 )
405 << "</comment>\n";
406
407 for ( i = inv.begin(); i != inv.end(); ++i ) {
408 // no need for such noise
409 if ( (*i).type() == MetaTranslatorMessage::Obsolete &&
410 (*i).translation().isEmpty() )
411 continue;
412
413 t << " <message";
414 if ( (*i).utf8() )
415 t << " encoding=\"UTF-8\"";
416 t << ">\n"
417 << " <source>" << evilBytes( (*i).sourceText(),
418 (*i).utf8() )
419 << "</source>\n";
420 if ( !QCString((*i).comment()).isEmpty() )
421 t << " <comment>" << evilBytes( (*i).comment(),
422 (*i).utf8() )
423 << "</comment>\n";
424 t << " <translation";
425 if ( (*i).type() == MetaTranslatorMessage::Unfinished )
426 t << " type=\"unfinished\"";
427 else if ( (*i).type() == MetaTranslatorMessage::Obsolete )
428 t << " type=\"obsolete\"";
429 t << ">" << protect( (*i).translation().utf8() )
430 << "</translation>\n";
431 t << " </message>\n";
432 }
433 t << "</context>\n";
434 }
435 t << "</TS>\n";
436 f.close();
437 return TRUE;
438}
439
440bool MetaTranslator::release( const QString& filename, bool verbose ) const
441{
442 QTranslator tor( 0 );
443 int finished = 0;
444 int unfinished = 0;
445 int untranslated = 0;
446 TMM::ConstIterator m;
447
448 for ( m = mm.begin(); m != mm.end(); ++m ) {
449 if ( m.key().type() != MetaTranslatorMessage::Obsolete ) {
450 if ( m.key().translation().isEmpty() ) {
451 untranslated++;
452 } else {
453 if ( m.key().type() == MetaTranslatorMessage::Unfinished )
454 unfinished++;
455 else
456 finished++;
457
458 QCString context = m.key().context();
459 QCString sourceText = m.key().sourceText();
460 QCString comment = m.key().comment();
461 QString translation = m.key().translation();
462
463 /*
464 Drop the comment in (context, sourceText, comment),
465 unless (context, sourceText, "") already exists, or
466 unless we already dropped the comment of (context,
467 sourceText, comment0).
468 */
469 if ( comment.isEmpty()
470 || contains(context, sourceText, "")
471 || !tor.findMessage(context, sourceText, "").translation()
472 .isNull() ) {
473 tor.insert( m.key() );
474 } else {
475 tor.insert( QTranslatorMessage(context, sourceText, "",
476 translation) );
477 }
478 }
479 }
480 }
481
482 bool saved = tor.save( filename, QTranslator::Stripped );
483 if ( saved && verbose )
484 fprintf( stderr,
485 " %d finished, %d unfinished and %d untranslated messages\n",
486 finished, unfinished, untranslated );
487
488 return saved;
489}
490
491bool MetaTranslator::contains( const char *context, const char *sourceText,
492 const char *comment ) const
493{
494 return mm.find( MetaTranslatorMessage(context, sourceText, comment) ) !=
495 mm.end();
496}
497
498void MetaTranslator::insert( const MetaTranslatorMessage& m )
499{
500 int pos = mm.count();
501 TMM::Iterator n = mm.find( m );
502 if ( n != mm.end() )
503 pos = *n;
504 mm.replace( m, pos );
505}
506
507void MetaTranslator::stripObsoleteMessages()
508{
509 TMM newmm;
510
511 TMM::Iterator m = mm.begin();
512 while ( m != mm.end() ) {
513 if ( m.key().type() != MetaTranslatorMessage::Obsolete )
514 newmm.insert( m.key(), *m );
515 ++m;
516 }
517 mm = newmm;
518}
519
520void MetaTranslator::stripEmptyContexts()
521{
522 TMM newmm;
523
524 TMM::Iterator m = mm.begin();
525 while ( m != mm.end() ) {
526 if ( QCString(m.key().sourceText()).isEmpty() ) {
527 TMM::Iterator n = m;
528 ++n;
529 // the context comment is followed by other messages
530 if ( n != newmm.end() &&
531 qstrcmp(m.key().context(), n.key().context()) == 0 )
532 newmm.insert( m.key(), *m );
533 } else {
534 newmm.insert( m.key(), *m );
535 }
536 ++m;
537 }
538 mm = newmm;
539}
540
541void MetaTranslator::setCodec( const char *name )
542{
543 const int latin1 = 4;
544
545 codecName = name;
546 codec = QTextCodec::codecForName( name );
547 if ( codec == 0 || codec->mibEnum() == latin1 )
548 codec = 0;
549}
550
551QString MetaTranslator::toUnicode( const char *str, bool utf8 ) const
552{
553 if ( utf8 )
554 return QString::fromUtf8( str );
555 else if ( codec == 0 )
556 return QString( str );
557 else
558 return codec->toUnicode( str );
559}
560
561QValueList<MetaTranslatorMessage> MetaTranslator::messages() const
562{
563 int n = mm.count();
564 TMM::ConstIterator *t = new TMM::ConstIterator[n + 1];
565 TMM::ConstIterator m;
566 for ( m = mm.begin(); m != mm.end(); ++m )
567 t[*m] = m;
568
569 QValueList<MetaTranslatorMessage> val;
570 for ( int i = 0; i < n; i++ )
571 val.append( t[i].key() );
572
573 delete[] t;
574 return val;
575}
576
577QValueList<MetaTranslatorMessage> MetaTranslator::translatedMessages() const
578{
579 QValueList<MetaTranslatorMessage> val;
580 TMM::ConstIterator m;
581 for ( m = mm.begin(); m != mm.end(); ++m ) {
582 if ( m.key().type() == MetaTranslatorMessage::Finished )
583 val.append( m.key() );
584 }
585 return val;
586}
diff --git a/development/translation/shared/metatranslator.h b/development/translation/shared/metatranslator.h
new file mode 100644
index 0000000..d35b202
--- a/dev/null
+++ b/development/translation/shared/metatranslator.h
@@ -0,0 +1,99 @@
1/**********************************************************************
2** Copyright (C) 2000-2002 Trolltech AS. All rights reserved.
3**
4** This file is part of Qt Linguist.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20
21#ifndef METATRANSLATOR_H
22#define METATRANSLATOR_H
23
24#include <qmap.h>
25#include <qstring.h>
26#include <qtranslator.h>
27#include <qvaluelist.h>
28
29class QTextCodec;
30
31class MetaTranslatorMessage : public QTranslatorMessage
32{
33public:
34 enum Type { Unfinished, Finished, Obsolete };
35
36 MetaTranslatorMessage();
37 MetaTranslatorMessage( const char *context, const char *sourceText,
38 const char *comment,
39 const QString& translation = QString::null,
40 bool utf8 = FALSE, Type type = Unfinished );
41 MetaTranslatorMessage( const MetaTranslatorMessage& m );
42
43 MetaTranslatorMessage& operator=( const MetaTranslatorMessage& m );
44
45 void setType( Type nt ) { ty = nt; }
46 Type type() const { return ty; }
47 bool utf8() const { return utfeight; }
48
49 bool operator==( const MetaTranslatorMessage& m ) const;
50 bool operator!=( const MetaTranslatorMessage& m ) const
51 { return !operator==( m ); }
52 bool operator<( const MetaTranslatorMessage& m ) const;
53 bool operator<=( const MetaTranslatorMessage& m )
54 { return !operator>( m ); }
55 bool operator>( const MetaTranslatorMessage& m ) const
56 { return this->operator<( m ); }
57 bool operator>=( const MetaTranslatorMessage& m ) const
58 { return !operator<( m ); }
59
60private:
61 bool utfeight;
62 Type ty;
63};
64
65class MetaTranslator
66{
67public:
68 MetaTranslator();
69 MetaTranslator( const MetaTranslator& tor );
70
71 MetaTranslator& operator=( const MetaTranslator& tor );
72
73 bool load( const QString& filename );
74 bool save( const QString& filename ) const;
75 bool release( const QString& filename, bool verbose = FALSE ) const;
76
77 bool contains( const char *context, const char *sourceText,
78 const char *comment ) const;
79 void insert( const MetaTranslatorMessage& m );
80
81 void stripObsoleteMessages();
82 void stripEmptyContexts();
83
84 void setCodec( const char *name );
85 QString toUnicode( const char *str, bool utf8 ) const;
86
87 QValueList<MetaTranslatorMessage> messages() const;
88 QValueList<MetaTranslatorMessage> translatedMessages() const;
89
90private:
91 typedef QMap<MetaTranslatorMessage, int> TMM;
92 typedef QMap<int, MetaTranslatorMessage> TMMInv;
93
94 TMM mm;
95 QCString codecName;
96 QTextCodec *codec;
97};
98
99#endif
diff --git a/development/translation/shared/opie.cpp b/development/translation/shared/opie.cpp
new file mode 100644
index 0000000..c5c72d1
--- a/dev/null
+++ b/development/translation/shared/opie.cpp
@@ -0,0 +1,40 @@
1#include <stdlib.h>
2
3#include <qdir.h>
4
5#include "opie.h"
6
7OPIE* OPIE::m_self = 0;
8
9
10OPIE::OPIE() {
11}
12OPIE::~OPIE() {
13}
14OPIE* OPIE::self() {
15 if (!m_self ) m_self = new OPIE;
16 return m_self;
17}
18QStringList OPIE::languageList( const QString& _opieDir )const {
19 QString opieDi = opieDir( _opieDir );
20
21 QStringList langs;
22 QDir dir( opieDi + "/i18n/");
23 if (!dir.exists() ) return langs;
24 langs = dir.entryList( QDir::Dirs );
25
26 langs.remove("CVS"); // hey this no language
27 langs.remove("unmaintained"); // remove this one too
28 langs.remove(".");
29 langs.remove("..");
30
31
32
33 return langs;
34}
35QString OPIE::opieDir( const QString& _opieDir ) const{
36 if (!_opieDir.isEmpty() ) return _opieDir;
37 char* dir = ::getenv("OPIEDIR");
38 if (!dir ) return QString::null;
39 return QString::fromLatin1(dir);
40}
diff --git a/development/translation/shared/opie.h b/development/translation/shared/opie.h
new file mode 100644
index 0000000..4646bb0
--- a/dev/null
+++ b/development/translation/shared/opie.h
@@ -0,0 +1,21 @@
1#ifndef OPIE_H
2#define OPIE_H
3
4#include <qstring.h>
5#include <qstringlist.h>
6
7class OPIE {
8public:
9 static OPIE* self();
10 /** get the list of languages */
11 QStringList languageList(const QString& opiedir = QString::null)const;
12 QString opieDir(const QString& opieDir)const;
13
14private:
15 OPIE();
16 ~OPIE();
17 static OPIE* m_self;
18
19};
20
21#endif
diff --git a/development/translation/shared/proparser.cpp b/development/translation/shared/proparser.cpp
new file mode 100644
index 0000000..21d2f86
--- a/dev/null
+++ b/development/translation/shared/proparser.cpp
@@ -0,0 +1,87 @@
1/**********************************************************************
2** Copyright (C) 2000-2002 Trolltech AS. All rights reserved.
3**
4** This file is part of Qt Linguist.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20
21#include "proparser.h"
22
23#include <qregexp.h>
24#include <qstringlist.h>
25
26QMap<QString, QString> proFileTagMap( const QString& text )
27{
28 QString t = text;
29
30 /*
31 Strip comments, merge lines ending with backslash, add
32 spaces around '=' and '+=', replace '\n' with ';', and
33 simplify white spaces.
34 */
35 t.replace( QRegExp(QString("#[^\n]$")), QString(" ") );
36 t.replace( QRegExp(QString("\\\\\\s*\n")), QString(" ") );
37 t.replace( "=", QString(" = ") );
38 t.replace( "+ =", QString(" += ") );
39 t.replace( "\n", QString(";") );
40 t = t.simplifyWhiteSpace();
41
42 QMap<QString, QString> tagMap;
43
44 QStringList lines = QStringList::split( QChar(';'), t );
45 QStringList::Iterator line;
46 for ( line = lines.begin(); line != lines.end(); ++line ) {
47 QStringList toks = QStringList::split( QChar(' '), *line );
48
49 if ( toks.count() >= 3 &&
50 (toks[1] == QString("=") || toks[1] == QString("+=")) ) {
51 QString tag = toks.first();
52 int k = tag.findRev( QChar(':') ); // as in 'unix:'
53 if ( k != -1 )
54 tag = tag.mid( k + 1 );
55 toks.remove( toks.begin() );
56
57 QString action = toks.first();
58 toks.remove( toks.begin() );
59
60 if ( tagMap.contains(tag) ) {
61 if ( action == QString("=") )
62 tagMap.replace( tag, toks.join(QChar(' ')) );
63 else
64 tagMap[tag] += QChar( ' ' ) + toks.join( QChar(' ') );
65 } else {
66 tagMap[tag] = toks.join( QChar(' ') );
67 }
68 }
69 }
70
71 QRegExp var( "\\$\\$[a-zA-Z0-9_]+" );
72 QMap<QString, QString>::Iterator it;
73 for ( it = tagMap.begin(); it != tagMap.end(); ++it ) {
74 int i = 0;
75
76 while ( (i = var.search(it.data(), i)) != -1 ) {
77 int len = var.matchedLength();
78 QString invocation = (*it).mid( i + 2, len - 2 );
79 QString after;
80 if ( tagMap.contains(invocation) )
81 after = tagMap[invocation];
82 (*it).replace( i, len, after );
83 i += after.length();
84 }
85 }
86 return tagMap;
87}
diff --git a/development/translation/shared/proparser.h b/development/translation/shared/proparser.h
new file mode 100644
index 0000000..6a61d90
--- a/dev/null
+++ b/development/translation/shared/proparser.h
@@ -0,0 +1,29 @@
1/**********************************************************************
2** Copyright (C) 2000 Trolltech AS. All rights reserved.
3**
4** This file is part of Qt Linguist.
5**
6** This file may be distributed and/or modified under the terms of the
7** GNU General Public License version 2 as published by the Free Software
8** Foundation and appearing in the file LICENSE.GPL included in the
9** packaging of this file.
10**
11** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13**
14** See http://www.trolltech.com/gpl/ for GPL licensing information.
15**
16** Contact info@trolltech.com if any conditions of this licensing are
17** not clear to you.
18**
19**********************************************************************/
20
21#ifndef PROPARSER_H
22#define PROPARSER_H
23
24#include <qmap.h>
25#include <qstring.h>
26
27QMap<QString, QString> proFileTagMap( const QString& text );
28
29#endif