summaryrefslogtreecommitdiff
path: root/development/translation/opie-lupdate
Unidiff
Diffstat (limited to 'development/translation/opie-lupdate') (more/less context) (ignore whitespace changes)
-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
7 files changed, 1454 insertions, 0 deletions
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}