author | zecke <zecke> | 2004-01-09 19:13:35 (UTC) |
---|---|---|
committer | zecke <zecke> | 2004-01-09 19:13:35 (UTC) |
commit | 56bb9961db1158250bbfe971f4556b56c0bd6581 (patch) (side-by-side diff) | |
tree | a4f40da98f695b5aadd845b68aaab41ac3d81826 | |
parent | 6875988077013544246ae0df7cc2e5d538a7f152 (diff) | |
download | opie-56bb9961db1158250bbfe971f4556b56c0bd6581.zip opie-56bb9961db1158250bbfe971f4556b56c0bd6581.tar.gz opie-56bb9961db1158250bbfe971f4556b56c0bd6581.tar.bz2 |
Update lupdate and shared to Qt3.3 qt-copy sources
Some special replacement for $$(OPIEDIR) to make ar_* happy
-rw-r--r-- | development/translation/opie-lupdate/fetchtr.cpp | 55 | ||||
-rw-r--r-- | development/translation/opie-lupdate/main.cpp | 11 | ||||
-rw-r--r-- | development/translation/opie-lupdate/merge.cpp | 6 | ||||
-rw-r--r-- | development/translation/opie-lupdate/numberh.cpp | 28 | ||||
-rw-r--r-- | development/translation/opie-lupdate/sametexth.cpp | 6 | ||||
-rw-r--r-- | development/translation/shared/metatranslator.cpp | 34 | ||||
-rw-r--r-- | development/translation/shared/metatranslator.h | 16 | ||||
-rw-r--r-- | development/translation/shared/opie.h | 2 | ||||
-rw-r--r-- | development/translation/shared/proparser.cpp | 111 | ||||
-rw-r--r-- | development/translation/shared/proparser.h | 8 |
10 files changed, 228 insertions, 49 deletions
diff --git a/development/translation/opie-lupdate/fetchtr.cpp b/development/translation/opie-lupdate/fetchtr.cpp index d1f5881..a137628 100644 --- a/development/translation/opie-lupdate/fetchtr.cpp +++ b/development/translation/opie-lupdate/fetchtr.cpp @@ -1,466 +1,501 @@ /********************************************************************** ** Copyright (C) 2000 Trolltech AS. All rights reserved. ** ** This file is part of Qt Linguist. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include <metatranslator.h> #include <qfile.h> #include <qregexp.h> #include <qstring.h> #include <qtextstream.h> #include <qvaluestack.h> #include <qxml.h> #include <ctype.h> #include <errno.h> #include <stdio.h> #include <string.h> /* qmake ignore Q_OBJECT */ static const char MagicComment[] = "TRANSLATOR "; static QMap<QCString, int> needs_Q_OBJECT; static QMap<QCString, int> lacks_Q_OBJECT; /* The first part of this source file is the C++ tokenizer. We skip most of C++; the only tokens that interest us are defined here. Thus, the code fragment int main() { printf( "Hello, world!\n" ); return 0; } is broken down into the following tokens (Tok_ omitted): Ident Ident LeftParen RightParen LeftBrace Ident LeftParen String RightParen Semicolon return Semicolon RightBrace. The 0 doesn't produce any token. */ enum { Tok_Eof, Tok_class, Tok_namespace, Tok_return, Tok_tr, Tok_trUtf8, Tok_translate, Tok_Q_OBJECT, Tok_Ident, Tok_Comment, Tok_String, Tok_Arrow, Tok_Colon, Tok_Gulbrandsen, Tok_LeftBrace, Tok_RightBrace, Tok_LeftParen, Tok_RightParen, Tok_Comma, Tok_Semicolon }; /* The tokenizer maintains the following global variables. The names should be self-explanatory. */ static QCString yyFileName; static int yyCh; static char yyIdent[128]; static size_t yyIdentLen; static char yyComment[65536]; static size_t yyCommentLen; static char yyString[16384]; static size_t yyStringLen; static QValueStack<int> yySavedBraceDepth; +static QValueStack<int> yySavedParenDepth; static int yyBraceDepth; static int yyParenDepth; static int yyLineNo; static int yyCurLineNo; +static int yyBraceLineNo; +static int yyParenLineNo; // the file to read from (if reading from a file) static FILE *yyInFile; // the string to read from and current position in the string (otherwise) static QString yyInStr; static int yyInPos; static int (*getChar)(); static int getCharFromFile() { int c = getc( yyInFile ); if ( c == '\n' ) yyCurLineNo++; return c; } static int getCharFromString() { if ( yyInPos == (int) yyInStr.length() ) { return EOF; } else { return yyInStr[yyInPos++].latin1(); } } static void startTokenizer( const char *fileName, int (*getCharFunc)() ) { yyInPos = 0; getChar = getCharFunc; yyFileName = fileName; yyCh = getChar(); yySavedBraceDepth.clear(); + yySavedParenDepth.clear(); yyBraceDepth = 0; yyParenDepth = 0; yyCurLineNo = 1; + yyBraceLineNo = 1; + yyParenLineNo = 1; } static int getToken() { const char tab[] = "abfnrtv"; const char backTab[] = "\a\b\f\n\r\t\v"; uint n; yyIdentLen = 0; yyCommentLen = 0; yyStringLen = 0; while ( yyCh != EOF ) { yyLineNo = yyCurLineNo; if ( isalpha(yyCh) || yyCh == '_' ) { do { if ( yyIdentLen < sizeof(yyIdent) - 1 ) yyIdent[yyIdentLen++] = (char) yyCh; yyCh = getChar(); } while ( isalnum(yyCh) || yyCh == '_' ); yyIdent[yyIdentLen] = '\0'; switch ( yyIdent[0] ) { case 'Q': if ( strcmp(yyIdent + 1, "_OBJECT") == 0 ) { return Tok_Q_OBJECT; } else if ( strcmp(yyIdent + 1, "T_TR_NOOP") == 0 ) { return Tok_tr; } else if ( strcmp(yyIdent + 1, "T_TRANSLATE_NOOP") == 0 ) { return Tok_translate; } break; case 'T': // TR() for when all else fails if ( qstricmp(yyIdent + 1, "R") == 0 ) return Tok_tr; break; case 'c': if ( strcmp(yyIdent + 1, "lass") == 0 ) return Tok_class; break; + case 'f': + /* + QTranslator::findMessage() has the same parameters as + QApplication::translate(). + */ + if ( strcmp(yyIdent + 1, "indMessage") == 0 ) + return Tok_translate; + break; + case 'i': + /* FOR KDE APPS */ + if ( strcmp( yyIdent + 1, "8n") == 0 ) + return Tok_translate; + break; case 'n': if ( strcmp(yyIdent + 1, "amespace") == 0 ) return Tok_namespace; break; case 'r': if ( strcmp(yyIdent + 1, "eturn") == 0 ) return Tok_return; break; case 's': if ( strcmp(yyIdent + 1, "truct") == 0 ) return Tok_class; break; - case 'i': - if( strcmp(yyIdent + 1, "18n") == 0 ) - return Tok_tr; case 't': if ( strcmp(yyIdent + 1, "r") == 0 ) { return Tok_tr; } else if ( qstrcmp(yyIdent + 1, "rUtf8") == 0 ) { return Tok_trUtf8; } else if ( qstrcmp(yyIdent + 1, "ranslate") == 0 ) { return Tok_translate; } } return Tok_Ident; } else { switch ( yyCh ) { case '#': /* Early versions of lupdate complained about unbalanced braces in the following code: #ifdef ALPHA while ( beta ) { #else while ( gamma ) { #endif delta; } The code contains, indeed, two opening braces for one closing brace; yet there's no reason to panic. The solution is to remember yyBraceDepth as it was when #if, #ifdef or #ifndef was met, and to set yyBraceDepth to that value when meeting #elif or #else. */ do { yyCh = getChar(); } while ( isspace(yyCh) && yyCh != '\n' ); switch ( yyCh ) { case 'i': yyCh = getChar(); if ( yyCh == 'f' ) { // if, ifdef, ifndef yySavedBraceDepth.push( yyBraceDepth ); + yySavedParenDepth.push( yyParenDepth ); } break; case 'e': yyCh = getChar(); if ( yyCh == 'l' ) { // elif, else - if ( !yySavedBraceDepth.isEmpty() ) + if ( !yySavedBraceDepth.isEmpty() ) { yyBraceDepth = yySavedBraceDepth.top(); + yyParenDepth = yySavedParenDepth.top(); + } } else if ( yyCh == 'n' ) { // endif - if ( !yySavedBraceDepth.isEmpty() ) + if ( !yySavedBraceDepth.isEmpty() ) { yySavedBraceDepth.pop(); + yySavedParenDepth.pop(); + } } } while ( isalnum(yyCh) || yyCh == '_' ) yyCh = getChar(); break; case '/': yyCh = getChar(); if ( yyCh == '/' ) { do { yyCh = getChar(); } while ( yyCh != EOF && yyCh != '\n' ); } else if ( yyCh == '*' ) { bool metAster = FALSE; bool metAsterSlash = FALSE; while ( !metAsterSlash ) { yyCh = getChar(); if ( yyCh == EOF ) { fprintf( stderr, "%s: Unterminated C++ comment starting at" " line %d\n", (const char *) yyFileName, yyLineNo ); yyComment[yyCommentLen] = '\0'; return Tok_Comment; } if ( yyCommentLen < sizeof(yyComment) - 1 ) yyComment[yyCommentLen++] = (char) yyCh; if ( yyCh == '*' ) metAster = TRUE; else if ( metAster && yyCh == '/' ) metAsterSlash = TRUE; else metAster = FALSE; } yyCh = getChar(); yyCommentLen -= 2; yyComment[yyCommentLen] = '\0'; return Tok_Comment; } break; case '"': yyCh = getChar(); while ( yyCh != EOF && yyCh != '\n' && yyCh != '"' ) { if ( yyCh == '\\' ) { yyCh = getChar(); if ( yyCh == '\n' ) { yyCh = getChar(); } else if ( yyCh == 'x' ) { QCString hex = "0"; yyCh = getChar(); while ( isxdigit(yyCh) ) { hex += (char) yyCh; yyCh = getChar(); } sscanf( hex, "%x", &n ); if ( yyStringLen < sizeof(yyString) - 1 ) yyString[yyStringLen++] = (char) n; } else if ( yyCh >= '0' && yyCh < '8' ) { QCString oct = ""; do { oct += (char) yyCh; yyCh = getChar(); } while ( yyCh >= '0' && yyCh < '8' ); sscanf( oct, "%o", &n ); if ( yyStringLen < sizeof(yyString) - 1 ) yyString[yyStringLen++] = (char) n; } else { const char *p = strchr( tab, yyCh ); if ( yyStringLen < sizeof(yyString) - 1 ) yyString[yyStringLen++] = ( p == 0 ) ? (char) yyCh : backTab[p - tab]; yyCh = getChar(); } } else { if ( yyStringLen < sizeof(yyString) - 1 ) yyString[yyStringLen++] = (char) yyCh; yyCh = getChar(); } } yyString[yyStringLen] = '\0'; if ( yyCh != '"' ) qWarning( "%s:%d: Unterminated C++ string", (const char *) yyFileName, yyLineNo ); if ( yyCh == EOF ) { return Tok_Eof; } else { yyCh = getChar(); return Tok_String; } break; case '-': yyCh = getChar(); if ( yyCh == '>' ) { yyCh = getChar(); return Tok_Arrow; } break; case ':': yyCh = getChar(); if ( yyCh == ':' ) { yyCh = getChar(); return Tok_Gulbrandsen; } return Tok_Colon; case '\'': yyCh = getChar(); if ( yyCh == '\\' ) yyCh = getChar(); do { yyCh = getChar(); } while ( yyCh != EOF && yyCh != '\'' ); yyCh = getChar(); break; case '{': + if (yyBraceDepth == 0) + yyBraceLineNo = yyCurLineNo; yyBraceDepth++; yyCh = getChar(); return Tok_LeftBrace; case '}': + if (yyBraceDepth == 0) + yyBraceLineNo = yyCurLineNo; yyBraceDepth--; yyCh = getChar(); return Tok_RightBrace; case '(': + if (yyParenDepth == 0) + yyParenLineNo = yyCurLineNo; yyParenDepth++; yyCh = getChar(); return Tok_LeftParen; case ')': + if (yyParenDepth == 0) + yyParenLineNo = yyCurLineNo; yyParenDepth--; yyCh = getChar(); return Tok_RightParen; case ',': yyCh = getChar(); return Tok_Comma; case ';': yyCh = getChar(); return Tok_Semicolon; default: yyCh = getChar(); } } } return Tok_Eof; } /* The second part of this source file is the parser. It accomplishes a very easy task: It finds all strings inside a tr() or translate() call, and possibly finds out the context of the call. It supports three cases: (1) the context is specified, as in FunnyDialog::tr("Hello") or translate("FunnyDialog", "Hello"); (2) the call appears within an inlined function; (3) the call appears within a function defined outside the class definition. */ static int yyTok; static bool match( int t ) { bool matches = ( yyTok == t ); if ( matches ) yyTok = getToken(); return matches; } static bool matchString( QCString *s ) { bool matches = ( yyTok == Tok_String ); *s = ""; while ( yyTok == Tok_String ) { *s += yyString; yyTok = getToken(); } return matches; } static bool matchEncoding( bool *utf8 ) { if ( yyTok == Tok_Ident ) { if ( strcmp(yyIdent, "QApplication") == 0 ) { yyTok = getToken(); if ( yyTok == Tok_Gulbrandsen ) yyTok = getToken(); } *utf8 = QString( yyIdent ).endsWith( QString("UTF8") ); yyTok = getToken(); return TRUE; } else { return FALSE; } } static void parse( MetaTranslator *tor, const char *initialContext, const char *defaultContext ) { QMap<QCString, QCString> qualifiedContexts; QStringList namespaces; QCString context; QCString text; QCString com; QCString functionContext = initialContext; QCString prefix; bool utf8 = FALSE; bool missing_Q_OBJECT = FALSE; yyTok = getToken(); while ( yyTok != Tok_Eof ) { switch ( yyTok ) { case Tok_class: /* Partial support for inlined functions. */ yyTok = getToken(); if ( yyBraceDepth == (int) namespaces.count() && yyParenDepth == 0 ) { do { /* This code should execute only once, but we play safe with impure definitions such as 'class Q_EXPORT QMessageBox', in which case 'QMessageBox' is the class name, not 'Q_EXPORT'. */ functionContext = yyIdent; yyTok = getToken(); @@ -528,200 +563,200 @@ static void parse( MetaTranslator *tor, const char *initialContext, utf8 = FALSE; yyTok = getToken(); if ( match(Tok_LeftParen) && matchString(&context) && match(Tok_Comma) && matchString(&text) ) { com = ""; if ( match(Tok_RightParen) || (match(Tok_Comma) && matchString(&com) && (match(Tok_RightParen) || match(Tok_Comma) && matchEncoding(&utf8) && match(Tok_RightParen))) ) tor->insert( MetaTranslatorMessage(context, text, com, QString::null, utf8) ); } break; case Tok_Q_OBJECT: missing_Q_OBJECT = FALSE; yyTok = getToken(); break; case Tok_Ident: if ( !prefix.isNull() ) prefix += "::"; prefix += yyIdent; yyTok = getToken(); if ( yyTok != Tok_Gulbrandsen ) prefix = (const char *) 0; break; case Tok_Comment: com = yyComment; com = com.simplifyWhiteSpace(); if ( com.left(sizeof(MagicComment) - 1) == MagicComment ) { com.remove( 0, sizeof(MagicComment) - 1 ); int k = com.find( ' ' ); if ( k == -1 ) { context = com; } else { context = com.left( k ); com.remove( 0, k + 1 ); tor->insert( MetaTranslatorMessage(context, "", com, QString::null, FALSE) ); } /* Provide a backdoor for people using "using namespace". See the manual for details. */ k = 0; while ( (k = context.find("::", k)) != -1 ) { qualifiedContexts.insert( context.mid(k + 2), context ); k++; } } yyTok = getToken(); break; case Tok_Arrow: yyTok = getToken(); if ( yyTok == Tok_tr || yyTok == Tok_trUtf8 ) qWarning( "%s:%d: Cannot invoke tr() like this", (const char *) yyFileName, yyLineNo ); break; case Tok_Gulbrandsen: // at top level? if ( yyBraceDepth == (int) namespaces.count() && yyParenDepth == 0 ) functionContext = prefix; yyTok = getToken(); break; case Tok_RightBrace: case Tok_Semicolon: if ( yyBraceDepth >= 0 && yyBraceDepth + 1 == (int) namespaces.count() ) namespaces.remove( namespaces.fromLast() ); if ( yyBraceDepth == (int) namespaces.count() ) { if ( missing_Q_OBJECT ) { if ( needs_Q_OBJECT.contains(functionContext) ) { qWarning( "%s:%d: Class '%s' lacks Q_OBJECT macro", (const char *) yyFileName, yyLineNo, (const char *) functionContext ); } else { lacks_Q_OBJECT.insert( functionContext, 0 ); } } functionContext = defaultContext; missing_Q_OBJECT = FALSE; } yyTok = getToken(); break; default: yyTok = getToken(); } } if ( yyBraceDepth != 0 ) fprintf( stderr, - "%s: Unbalanced braces in C++ code (or abuse of the C++" + "%s:%d: Unbalanced braces in C++ code (or abuse of the C++" " preprocessor)\n", - (const char *) yyFileName ); - if ( yyParenDepth != 0 ) + (const char *)yyFileName, yyBraceLineNo ); + else if ( yyParenDepth != 0 ) fprintf( stderr, - "%s: Unbalanced parentheses in C++ code (or abuse of the C++" + "%s:%d: Unbalanced parentheses in C++ code (or abuse of the C++" " preprocessor)\n", - (const char *) yyFileName ); + (const char *)yyFileName, yyParenLineNo ); } void fetchtr_cpp( const char *fileName, MetaTranslator *tor, const char *defaultContext, bool mustExist ) { yyInFile = fopen( fileName, "r" ); if ( yyInFile == 0 ) { if ( mustExist ) fprintf( stderr, "lupdate error: Cannot open C++ source file '%s': %s\n", fileName, strerror(errno) ); return; } startTokenizer( fileName, getCharFromFile ); parse( tor, 0, defaultContext ); fclose( yyInFile ); } /* In addition to C++, we support Qt Designer UI files. */ /* Fetches tr() calls in C++ code in UI files (inside "<function>" tag). This mechanism is obsolete. */ void fetchtr_inlined_cpp( const char *fileName, const QString& in, MetaTranslator *tor, const char *context ) { yyInStr = in; startTokenizer( fileName, getCharFromString ); parse( tor, context, 0 ); yyInStr = QString::null; } class UiHandler : public QXmlDefaultHandler { public: UiHandler( MetaTranslator *translator, const char *fileName ) : tor( translator ), fname( fileName ), comment( "" ) { } virtual bool startElement( const QString& namespaceURI, const QString& localName, const QString& qName, const QXmlAttributes& atts ); virtual bool endElement( const QString& namespaceURI, const QString& localName, const QString& qName ); virtual bool characters( const QString& ch ); virtual bool fatalError( const QXmlParseException& exception ); private: void flush(); MetaTranslator *tor; QCString fname; QString context; QString source; QString comment; QString accum; }; bool UiHandler::startElement( const QString& /* namespaceURI */, const QString& /* localName */, const QString& qName, const QXmlAttributes& atts ) { if ( qName == QString("item") ) { flush(); if ( !atts.value(QString("text")).isEmpty() ) source = atts.value( QString("text") ); } else if ( qName == QString("string") ) { flush(); } accum.truncate( 0 ); return TRUE; } bool UiHandler::endElement( const QString& /* namespaceURI */, const QString& /* localName */, const QString& qName ) { accum.replace( QRegExp(QString("\r\n")), "\n" ); if ( qName == QString("class") ) { if ( context.isEmpty() ) context = accum; } else if ( qName == QString("string") ) { source = accum; } else if ( qName == QString("comment") ) { comment = accum; flush(); } else if ( qName == QString("function") ) { fetchtr_inlined_cpp( (const char *) fname, accum, tor, context.latin1() ); } else { diff --git a/development/translation/opie-lupdate/main.cpp b/development/translation/opie-lupdate/main.cpp index ce65e7a..bf16fd7 100644 --- a/development/translation/opie-lupdate/main.cpp +++ b/development/translation/opie-lupdate/main.cpp @@ -1,215 +1,224 @@ /********************************************************************** ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** Copyright (C) 2003 zecke ** ** This file is part of Qt Linguist. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include <metatranslator.h> #include <proparser.h> #include <opie.h> #include <qfile.h> #include <qfileinfo.h> #include <qstring.h> #include <qstringlist.h> #include <qtextstream.h> #include <errno.h> #include <string.h> // defined in fetchtr.cpp extern void fetchtr_cpp( const char *fileName, MetaTranslator *tor, const char *defaultContext, bool mustExist ); extern void fetchtr_ui( const char *fileName, MetaTranslator *tor, const char *defaultContext, bool mustExist ); // defined in merge.cpp extern void merge( MetaTranslator *tor, const MetaTranslator *virginTor, bool verbose ); typedef QValueList<MetaTranslatorMessage> TML; static const char* LUPDATE_VERSION = "0.1"; static void printUsage() { fprintf( stderr, "Usage:\n" " opie-lupdate [options] project-file\n" " opie-lupdate [options] source-files -ts ts-files\n" "Options:\n" " -opie The OPIE base dir if not supplied $OPIEDIR will be taken\n" " -help Display this information and exit\n" " -noobsolete\n" " Drop all obsolete strings\n" " -verbose\n" " Explain what is being done\n" " -version\n" " Display the version of lupdate and exit\n" ); } +/*static QString opie_escape( const QString& str ) { + QString ret = str.stripWhiteSpace(); + qWarning(ret); + if ( ret.startsWith("$$(OPIEDIR)") ) + ret = ret.replace("$$(OPIEDIR)", OPIE::self()->opieDir() ); + qWarning(ret); + return ret; + }*/ + static void updateTsFiles( const MetaTranslator& fetchedTor, const QString& opiedir, const QStringList& languages, const QString& basename, const QString& codec, bool noObsolete, bool verbose ) { QStringList::ConstIterator it = languages.begin(); for ( ; it != languages.end(); ++it ) { QString fileName = opiedir + "/i18n/" + (*it) + "/" + basename; MetaTranslator tor; tor.load( fileName ); if ( !codec.isEmpty() ) tor.setCodec( codec ); if ( verbose ) fprintf( stderr, "Updating '%s'...\n", fileName.latin1() ); merge( &tor, &fetchedTor, verbose ); if ( noObsolete ) tor.stripObsoleteMessages(); tor.stripEmptyContexts(); if ( !tor.save(fileName) ) fprintf( stderr, "lupdate error: Cannot save '%s': %s\n", fileName.latin1(), strerror(errno) ); } } int main( int argc, char **argv ) { QString defaultContext = "@default"; MetaTranslator fetchedTor; QCString codec; QStringList tsFileNames; QString opiedir; QString translationBase; QString target; bool verbose = FALSE; bool noObsolete = FALSE; bool metSomething = FALSE; bool isLib = FALSE; int numFiles = 0; int i; QStringList languageList = OPIE::self()->languageList(opiedir); for ( i = 1; i < argc; i++ ) { if ( qstrcmp(argv[i], "-help") == 0 ) { printUsage(); return 0; } else if ( qstrcmp(argv[i], "-noobsolete") == 0 ) { noObsolete = TRUE; continue; } else if ( qstrcmp(argv[i], "-verbose") == 0 ) { verbose = TRUE; continue; } else if ( qstrcmp(argv[i], "-version") == 0 ) { fprintf( stderr, "lupdate version %s\n", LUPDATE_VERSION ); return 0; } else if ( qstrcmp(argv[i], "-opie") == 0 ) { if( i+1 < argc ) { opiedir = argv[i+1]; languageList = OPIE::self()->languageList(opiedir); } i++; // UGLY but we want to skip the next argument continue; } numFiles++; QString fullText; QFile f( argv[i] ); if ( !f.open(IO_ReadOnly) ) { fprintf( stderr, "lupdate error: Cannot open file '%s': %s\n", argv[i], strerror(errno) ); return 1; } QTextStream t( &f ); fullText = t.read(); f.close(); fetchedTor = MetaTranslator(); codec.truncate( 0 ); tsFileNames.clear(); isLib = FALSE; - QMap<QString, QString> tagMap = proFileTagMap( fullText ); + QMap<QString, QString> tagMap = proFileTagMap( fullText, OPIE::self()->opieDir() ); QMap<QString, QString>::Iterator it; for ( it = tagMap.begin(); it != tagMap.end(); ++it ) { QStringList toks = QStringList::split( ' ', it.data() ); QStringList::Iterator t; for ( t = toks.begin(); t != toks.end(); ++t ) { if ( it.key() == "HEADERS" || it.key() == "SOURCES" ) { fetchtr_cpp( *t, &fetchedTor, defaultContext, TRUE ); metSomething = TRUE; } else if ( it.key() == "INTERFACES" || it.key() == "FORMS" ) { fetchtr_ui( *t, &fetchedTor, defaultContext, TRUE ); fetchtr_cpp( *t + ".h", &fetchedTor, defaultContext, FALSE ); metSomething = TRUE; } else if ( it.key() == "TRANSLATIONS" ) { // we do not care for that attribute anymore //tsFileNames.append( *t ); metSomething = TRUE; } else if ( it.key() == "CODEC" ) { codec = (*t).latin1(); } else if ( it.key() == "TARGET" ) { target = *t; metSomething = TRUE; } else if ( it.key() == "TEMPLATE" ) { if ( (*t).stripWhiteSpace().lower() == "lib" ) isLib = true; } } } /** * We know the $OPIEDIR or have opiedir * we've a list of languages (de,en,gb,foo,bar) * we've got the TARGET and we no it's the lib * so let's do that * $OPIEDIR/language[i]/ifLibAppendLib$TARGET.ts */ qWarning("TARGET %s IsLib:%d", target.latin1(), isLib ); qWarning("LANGS %s", languageList.join(";").latin1() ); qWarning("OPIEDIR %s", OPIE::self()->opieDir(opiedir).latin1() ); if (isLib ) target.prepend("lib"); target += ".ts"; updateTsFiles( fetchedTor, OPIE::self()->opieDir(opiedir), languageList, target, codec, noObsolete, verbose ); if ( !metSomething ) { fprintf( stderr, "lupdate warning: File '%s' does not look like a" " project file\n", argv[i] ); } } if ( numFiles == 0 ) { printUsage(); return 1; } return 0; } diff --git a/development/translation/opie-lupdate/merge.cpp b/development/translation/opie-lupdate/merge.cpp index a96104e..3001d4b 100644 --- a/development/translation/opie-lupdate/merge.cpp +++ b/development/translation/opie-lupdate/merge.cpp @@ -1,110 +1,116 @@ /********************************************************************** ** Copyright (C) 2000 Trolltech AS. All rights reserved. ** ** This file is part of Qt Linguist. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include <metatranslator.h> // defined in numberh.cpp extern void applyNumberHeuristic( MetaTranslator *tor, bool verbose ); // defined in sametexth.cpp extern void applySameTextHeuristic( MetaTranslator *tor, bool verbose ); typedef QValueList<MetaTranslatorMessage> TML; /* Merges two MetaTranslator objects into the first one. The first one is a set of source texts and translations for a previous version of the internationalized program; the second one is a set of fresh source texts newly extracted from the source code, without any translation yet. */ void merge( MetaTranslator *tor, const MetaTranslator *virginTor, bool verbose ) { int known = 0; int neww = 0; int obsoleted = 0; TML all = tor->messages(); TML::Iterator it; /* The types of all the messages from the vernacular translator are updated according to the virgin translator. */ for ( it = all.begin(); it != all.end(); ++it ) { MetaTranslatorMessage::Type newType; MetaTranslatorMessage m = *it; // skip context comment if ( !QCString((*it).sourceText()).isEmpty() ) { if ( !virginTor->contains((*it).context(), (*it).sourceText(), (*it).comment()) ) { newType = MetaTranslatorMessage::Obsolete; if ( m.type() != MetaTranslatorMessage::Obsolete ) obsoleted++; } else { switch ( m.type() ) { case MetaTranslatorMessage::Finished: newType = MetaTranslatorMessage::Finished; known++; break; case MetaTranslatorMessage::Unfinished: newType = MetaTranslatorMessage::Unfinished; known++; break; case MetaTranslatorMessage::Obsolete: newType = MetaTranslatorMessage::Unfinished; neww++; } } if ( newType != m.type() ) { m.setType( newType ); tor->insert( m ); } } } /* Messages found only in the virgin translator are added to the vernacular translator. Among these are all the context comments. */ all = virginTor->messages(); for ( it = all.begin(); it != all.end(); ++it ) { if ( !tor->contains((*it).context(), (*it).sourceText(), (*it).comment()) ) { tor->insert( *it ); if ( !QCString((*it).sourceText()).isEmpty() ) neww++; } } /* The same-text heuristic handles cases where a message has an obsolete counterpart with a different context or comment. */ applySameTextHeuristic( tor, verbose ); /* The number heuristic handles cases where a message has an obsolete counterpart with mostly numbers differing in the source text. */ applyNumberHeuristic( tor, verbose ); diff --git a/development/translation/opie-lupdate/numberh.cpp b/development/translation/opie-lupdate/numberh.cpp index f7b7bf8..2f12c3d 100644 --- a/development/translation/opie-lupdate/numberh.cpp +++ b/development/translation/opie-lupdate/numberh.cpp @@ -1,235 +1,241 @@ /********************************************************************** ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** ** This file is part of Qt Linguist. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include <metatranslator.h> #include <qmemarray.h> #include <qcstring.h> #include <qmap.h> #include <qstringlist.h> #include <ctype.h> typedef QMap<QCString, MetaTranslatorMessage> TMM; typedef QValueList<MetaTranslatorMessage> TML; static bool isDigitFriendly( int c ) { - return ispunct( c ) || isspace( c ); + return ispunct((uchar)c) || isspace((uchar)c); } static int numberLength( const char *s ) { int i = 0; - if ( isdigit(s[0]) ) { + if ( isdigit((uchar)s[0]) ) { do { i++; - } while ( isdigit(s[i]) || - (isDigitFriendly(s[i]) && - (isdigit(s[i + 1]) || - (isDigitFriendly(s[i + 1]) && isdigit(s[i + 2])))) ); + } while (isdigit((uchar)s[i]) || + (isDigitFriendly(s[i]) && + (isdigit((uchar)s[i + 1]) || + (isDigitFriendly(s[i + 1]) && isdigit((uchar)s[i + 2]))))); } return i; } /* Returns a version of 'key' where all numbers have been replaced by zeroes. If there were none, returns "". */ static QCString zeroKey( const char *key ) { QCString zeroed( strlen(key) + 1 ); char *z = zeroed.data(); int i = 0, j = 0; int len; bool metSomething = FALSE; while ( key[i] != '\0' ) { len = numberLength( key + i ); if ( len > 0 ) { i += len; z[j++] = '0'; metSomething = TRUE; } else { z[j++] = key[i++]; } } z[j] = '\0'; if ( metSomething ) return zeroed; else return ""; } static QString translationAttempt( const QString& oldTranslation, const char *oldSource, const char *newSource ) { int p = zeroKey( oldSource ).contains( '0' ); int oldSourceLen = qstrlen( oldSource ); QString attempt; QStringList oldNumbers; QStringList newNumbers; QMemArray<bool> met( p ); QMemArray<int> matchedYet( p ); int i, j; int k = 0, ell, best; int m, n; int pass; /* This algorithm is hard to follow, so we'll consider an example all along: oldTranslation is "XeT 3.0", oldSource is "TeX 3.0" and newSource is "XeT 3.1". First, we set up two tables: oldNumbers and newNumbers. In our example, oldNumber[0] is "3.0" and newNumber[0] is "3.1". */ for ( i = 0, j = 0; i < oldSourceLen; i++, j++ ) { m = numberLength( oldSource + i ); n = numberLength( newSource + j ); if ( m > 0 ) { oldNumbers.append( QCString(oldSource + i, m + 1) ); newNumbers.append( QCString(newSource + j, n + 1) ); i += m; j += n; met[k] = FALSE; matchedYet[k] = 0; k++; } } /* We now go over the old translation, "XeT 3.0", one letter at a time, looking for numbers found in oldNumbers. Whenever such a number is met, it is replaced with its newNumber equivalent. In our example, the "3.0" of "XeT 3.0" becomes "3.1". */ for ( i = 0; i < (int) oldTranslation.length(); i++ ) { attempt += oldTranslation[i]; for ( k = 0; k < p; k++ ) { if ( oldTranslation[i] == oldNumbers[k][matchedYet[k]] ) matchedYet[k]++; else matchedYet[k] = 0; } /* Let's find out if the last character ended a match. We make two passes over the data. In the first pass, we try to match only numbers that weren't matched yet; if that fails, the second pass does the trick. This is useful in some suspicious cases, flagged below. */ for ( pass = 0; pass < 2; pass++ ) { best = p; // an impossible value for ( k = 0; k < p; k++ ) { if ( (!met[k] || pass > 0) && matchedYet[k] == (int) oldNumbers[k].length() && numberLength(oldTranslation.latin1() + (i + 1) - matchedYet[k]) == matchedYet[k] ) { // the longer the better if ( best == p || matchedYet[k] > matchedYet[best] ) best = k; } } if ( best != p ) { attempt.truncate( attempt.length() - matchedYet[best] ); attempt += newNumbers[best]; met[best] = TRUE; for ( k = 0; k < p; k++ ) matchedYet[k] = 0; break; } } } /* We flag two kinds of suspicious cases. They are identified as such with comments such as "{2000?}" at the end. Example of the first kind: old source text "TeX 3.0" translated as "XeT 2.0" is flagged "TeX 2.0 {3.0?}", no matter what the new text is. */ for ( k = 0; k < p; k++ ) { if ( !met[k] ) attempt += QString( " {" ) + newNumbers[k] + QString( "?}" ); } /* Example of the second kind: "1 of 1" translated as "1 af 1", with new source text "1 of 2", generates "1 af 2 {1 or 2?}" because it's not clear which of "1 af 2" and "2 af 1" is right. */ for ( k = 0; k < p; k++ ) { for ( ell = 0; ell < p; ell++ ) { if ( k != ell && oldNumbers[k] == oldNumbers[ell] && newNumbers[k] < newNumbers[ell] ) attempt += QString( " {" ) + newNumbers[k] + QString( " or " ) + newNumbers[ell] + QString( "?}" ); } } return attempt; } /* Augments a MetaTranslator with translations easily derived from similar existing (probably obsolete) translations. For example, if "TeX 3.0" is translated as "XeT 3.0" and "TeX 3.1" has no translation, "XeT 3.1" is added to the translator and is marked Unfinished. */ void applyNumberHeuristic( MetaTranslator *tor, bool verbose ) { TMM translated, untranslated; TMM::Iterator t, u; TML all = tor->messages(); TML::Iterator it; int inserted = 0; for ( it = all.begin(); it != all.end(); ++it ) { if ( (*it).type() == MetaTranslatorMessage::Unfinished ) { if ( (*it).translation().isEmpty() ) - untranslated.insert( zeroKey((*it).sourceText()), *it ); + untranslated.insert(QCString((*it).context()) + "\n" + (*it).sourceText() + "\n" + + (*it).comment(), *it); } else if ( !(*it).translation().isEmpty() ) { translated.insert( zeroKey((*it).sourceText()), *it ); } } for ( u = untranslated.begin(); u != untranslated.end(); ++u ) { - t = translated.find( u.key() ); + t = translated.find( zeroKey((*u).sourceText()) ); if ( t != translated.end() && !t.key().isEmpty() && qstrcmp((*t).sourceText(), (*u).sourceText()) != 0 ) { MetaTranslatorMessage m( *u ); - m.setTranslation( translationAttempt((*t).translation(), - (*t).sourceText(), - (*u).sourceText()) ); + m.setTranslation(translationAttempt((*t).translation(), (*t).sourceText(), + (*u).sourceText())); tor->insert( m ); inserted++; } } if ( verbose && inserted != 0 ) fprintf( stderr, " number heuristic provided %d translation%s\n", inserted, inserted == 1 ? "" : "s" ); } diff --git a/development/translation/opie-lupdate/sametexth.cpp b/development/translation/opie-lupdate/sametexth.cpp index 574cfd5..80909b3 100644 --- a/development/translation/opie-lupdate/sametexth.cpp +++ b/development/translation/opie-lupdate/sametexth.cpp @@ -1,84 +1,90 @@ /********************************************************************** ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** ** This file is part of Qt Linguist. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include <metatranslator.h> #include <qcstring.h> #include <qmap.h> typedef QMap<QCString, MetaTranslatorMessage> TMM; typedef QValueList<MetaTranslatorMessage> TML; /* Augments a MetaTranslator with trivially derived translations. For example, if "Enabled:" is consistendly translated as "Eingeschaltet:" no matter the context or the comment, "Eingeschaltet:" is added as the translation of any untranslated "Enabled:" text and is marked Unfinished. */ void applySameTextHeuristic( MetaTranslator *tor, bool verbose ) { TMM translated; TMM avoid; TMM::Iterator t; TML untranslated; TML::Iterator u; TML all = tor->messages(); TML::Iterator it; int inserted = 0; for ( it = all.begin(); it != all.end(); ++it ) { if ( (*it).type() == MetaTranslatorMessage::Unfinished ) { if ( (*it).translation().isEmpty() ) untranslated.append( *it ); } else { QCString key = (*it).sourceText(); t = translated.find( key ); if ( t != translated.end() ) { /* The same source text is translated at least two different ways. Do nothing then. */ if ( (*t).translation() != (*it).translation() ) { translated.remove( key ); avoid.insert( key, *it ); } } else if ( !avoid.contains(key) && !(*it).translation().isEmpty() ) { translated.insert( key, *it ); } } } for ( u = untranslated.begin(); u != untranslated.end(); ++u ) { QCString key = (*u).sourceText(); t = translated.find( key ); if ( t != translated.end() ) { MetaTranslatorMessage m( *u ); m.setTranslation( (*t).translation() ); tor->insert( m ); inserted++; } } if ( verbose && inserted != 0 ) fprintf( stderr, " same-text heuristic provided %d translation%s\n", inserted, inserted == 1 ? "" : "s" ); } diff --git a/development/translation/shared/metatranslator.cpp b/development/translation/shared/metatranslator.cpp index a01e1eb..51270c5 100644 --- a/development/translation/shared/metatranslator.cpp +++ b/development/translation/shared/metatranslator.cpp @@ -1,586 +1,596 @@ /********************************************************************** ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** ** This file is part of Qt Linguist. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include "metatranslator.h" #include <qapplication.h> #include <qcstring.h> #include <qfile.h> #include <qmessagebox.h> #include <qtextcodec.h> #include <qtextstream.h> #include <qxml.h> static bool encodingIsUtf8( const QXmlAttributes& atts ) { for ( int i = 0; i < atts.length(); i++ ) { // utf8="true" is a pre-3.0 syntax if ( atts.qName(i) == QString("utf8") ) { return ( atts.value(i) == QString("true") ); } else if ( atts.qName(i) == QString("encoding") ) { return ( atts.value(i) == QString("UTF-8") ); } } return FALSE; } class TsHandler : public QXmlDefaultHandler { public: TsHandler( MetaTranslator *translator ) : tor( translator ), type( MetaTranslatorMessage::Finished ), inMessage( FALSE ), ferrorCount( 0 ), contextIsUtf8( FALSE ), messageIsUtf8( FALSE ) { } virtual bool startElement( const QString& namespaceURI, const QString& localName, const QString& qName, const QXmlAttributes& atts ); virtual bool endElement( const QString& namespaceURI, const QString& localName, const QString& qName ); virtual bool characters( const QString& ch ); virtual bool fatalError( const QXmlParseException& exception ); private: MetaTranslator *tor; MetaTranslatorMessage::Type type; bool inMessage; QString context; QString source; QString comment; QString translation; QString accum; int ferrorCount; bool contextIsUtf8; bool messageIsUtf8; }; bool TsHandler::startElement( const QString& /* namespaceURI */, const QString& /* localName */, const QString& qName, const QXmlAttributes& atts ) { if ( qName == QString("byte") ) { for ( int i = 0; i < atts.length(); i++ ) { if ( atts.qName(i) == QString("value") ) { QString value = atts.value( i ); int base = 10; if ( value.startsWith("x") ) { base = 16; value = value.mid( 1 ); } int n = value.toUInt( 0, base ); if ( n != 0 ) accum += QChar( n ); } } } else { if ( qName == QString("context") ) { context.truncate( 0 ); source.truncate( 0 ); comment.truncate( 0 ); translation.truncate( 0 ); contextIsUtf8 = encodingIsUtf8( atts ); } else if ( qName == QString("message") ) { inMessage = TRUE; type = MetaTranslatorMessage::Finished; source.truncate( 0 ); comment.truncate( 0 ); translation.truncate( 0 ); messageIsUtf8 = encodingIsUtf8( atts ); } else if ( qName == QString("translation") ) { for ( int i = 0; i < atts.length(); i++ ) { if ( atts.qName(i) == QString("type") ) { if ( atts.value(i) == QString("unfinished") ) type = MetaTranslatorMessage::Unfinished; else if ( atts.value(i) == QString("obsolete") ) type = MetaTranslatorMessage::Obsolete; else type = MetaTranslatorMessage::Finished; } } } accum.truncate( 0 ); } return TRUE; } bool TsHandler::endElement( const QString& /* namespaceURI */, const QString& /* localName */, const QString& qName ) { if ( qName == QString("codec") || qName == QString("defaultcodec") ) { // "codec" is a pre-3.0 syntax tor->setCodec( accum ); } else if ( qName == QString("name") ) { context = accum; } else if ( qName == QString("source") ) { source = accum; } else if ( qName == QString("comment") ) { if ( inMessage ) { comment = accum; } else { if ( contextIsUtf8 ) - tor->insert( MetaTranslatorMessage(context.utf8(), "", + tor->insert( MetaTranslatorMessage(context.utf8(), + ContextComment, accum.utf8(), QString::null, TRUE, MetaTranslatorMessage::Unfinished) ); else - tor->insert( MetaTranslatorMessage(context.ascii(), "", + tor->insert( MetaTranslatorMessage(context.ascii(), + ContextComment, accum.ascii(), QString::null, FALSE, MetaTranslatorMessage::Unfinished) ); } } else if ( qName == QString("translation") ) { translation = accum; } else if ( qName == QString("message") ) { if ( messageIsUtf8 ) tor->insert( MetaTranslatorMessage(context.utf8(), source.utf8(), comment.utf8(), translation, TRUE, type) ); else tor->insert( MetaTranslatorMessage(context.ascii(), source.ascii(), comment.ascii(), translation, FALSE, type) ); inMessage = FALSE; } return TRUE; } bool TsHandler::characters( const QString& ch ) { QString t = ch; t.replace( "\r", "" ); accum += t; return TRUE; } bool TsHandler::fatalError( const QXmlParseException& exception ) { if ( ferrorCount++ == 0 ) { QString msg; msg.sprintf( "Parse error at line %d, column %d (%s).", exception.lineNumber(), exception.columnNumber(), exception.message().latin1() ); if ( qApp == 0 ) fprintf( stderr, "XML error: %s\n", msg.latin1() ); else QMessageBox::information( qApp->mainWidget(), QObject::tr("Qt Linguist"), msg ); } return FALSE; } static QString numericEntity( int ch ) { return QString( ch <= 0x20 ? "<byte value=\"x%1\"/>" : "&#x%1;" ) .arg( ch, 0, 16 ); } static QString protect( const QCString& str ) { QString result; int len = (int) str.length(); for ( int k = 0; k < len; k++ ) { switch( str[k] ) { case '\"': result += QString( """ ); break; case '&': result += QString( "&" ); break; case '>': result += QString( ">" ); break; case '<': result += QString( "<" ); break; case '\'': result += QString( "'" ); break; default: if ( (uchar) str[k] < 0x20 && str[k] != '\n' ) result += numericEntity( (uchar) str[k] ); else result += str[k]; } } return result; } static QString evilBytes( const QCString& str, bool utf8 ) { if ( utf8 ) { return protect( str ); } else { QString result; QCString t = protect( str ).latin1(); int len = (int) t.length(); for ( int k = 0; k < len; k++ ) { if ( (uchar) t[k] >= 0x7f ) result += numericEntity( (uchar) t[k] ); else result += QChar( t[k] ); } return result; } } MetaTranslatorMessage::MetaTranslatorMessage() : utfeight( FALSE ), ty( Unfinished ) { } MetaTranslatorMessage::MetaTranslatorMessage( const char *context, const char *sourceText, const char *comment, const QString& translation, bool utf8, Type type ) : QTranslatorMessage( context, sourceText, comment, translation ), utfeight( FALSE ), ty( type ) { /* Don't use UTF-8 if it makes no difference. UTF-8 should be reserved for the real problematic case: non-ASCII (possibly non-Latin-1) characters in .ui files. */ if ( utf8 ) { if ( sourceText != 0 ) { int i = 0; while ( sourceText[i] != '\0' ) { if ( (uchar) sourceText[i] >= 0x80 ) { utfeight = TRUE; break; } i++; } } if ( !utfeight && comment != 0 ) { int i = 0; while ( comment[i] != '\0' ) { if ( (uchar) comment[i] >= 0x80 ) { utfeight = TRUE; break; } i++; } } } } MetaTranslatorMessage::MetaTranslatorMessage( const MetaTranslatorMessage& m ) : QTranslatorMessage( m ), utfeight( m.utfeight ), ty( m.ty ) { } MetaTranslatorMessage& MetaTranslatorMessage::operator=( const MetaTranslatorMessage& m ) { QTranslatorMessage::operator=( m ); utfeight = m.utfeight; ty = m.ty; return *this; } bool MetaTranslatorMessage::operator==( const MetaTranslatorMessage& m ) const { return qstrcmp( context(), m.context() ) == 0 && qstrcmp( sourceText(), m.sourceText() ) == 0 && qstrcmp( comment(), m.comment() ) == 0; } bool MetaTranslatorMessage::operator<( const MetaTranslatorMessage& m ) const { int delta = qstrcmp( context(), m.context() ); if ( delta == 0 ) delta = qstrcmp( sourceText(), m.sourceText() ); if ( delta == 0 ) delta = qstrcmp( comment(), m.comment() ); return delta < 0; } MetaTranslator::MetaTranslator() - : codecName( "ISO-8859-1" ), codec( 0 ) { + clear(); } MetaTranslator::MetaTranslator( const MetaTranslator& tor ) : mm( tor.mm ), codecName( tor.codecName ), codec( tor.codec ) { - } MetaTranslator& MetaTranslator::operator=( const MetaTranslator& tor ) { mm = tor.mm; codecName = tor.codecName; codec = tor.codec; return *this; } -bool MetaTranslator::load( const QString& filename ) +void MetaTranslator::clear() { mm.clear(); + codecName = "ISO-8859-1"; + codec = 0; +} +bool MetaTranslator::load( const QString& filename ) +{ QFile f( filename ); if ( !f.open(IO_ReadOnly) ) return FALSE; QTextStream t( &f ); QXmlInputSource in( t ); QXmlSimpleReader reader; - // don't click on these! reader.setFeature( "http://xml.org/sax/features/namespaces", FALSE ); reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", TRUE ); reader.setFeature( "http://trolltech.com/xml/features/report-whitespace" "-only-CharData", FALSE ); QXmlDefaultHandler *hand = new TsHandler( this ); reader.setContentHandler( hand ); reader.setErrorHandler( hand ); bool ok = reader.parse( in ); reader.setContentHandler( 0 ); reader.setErrorHandler( 0 ); delete hand; f.close(); - if ( !ok ) - mm.clear(); return ok; } bool MetaTranslator::save( const QString& filename ) const { QFile f( filename ); if ( !f.open(IO_WriteOnly) ) return FALSE; QTextStream t( &f ); t.setCodec( QTextCodec::codecForName("ISO-8859-1") ); t << "<!DOCTYPE TS><TS>\n"; if ( codecName != "ISO-8859-1" ) t << "<defaultcodec>" << codecName << "</defaultcodec>\n"; TMM::ConstIterator m = mm.begin(); while ( m != mm.end() ) { TMMInv inv; TMMInv::Iterator i; bool contextIsUtf8 = m.key().utf8(); QCString context = m.key().context(); QCString comment = ""; do { - if ( QCString(m.key().sourceText()).isEmpty() ) { + if ( QCString(m.key().sourceText()) == ContextComment ) { if ( m.key().type() != MetaTranslatorMessage::Obsolete ) { contextIsUtf8 = m.key().utf8(); comment = QCString( m.key().comment() ); } } else { inv.insert( *m, m.key() ); } } while ( ++m != mm.end() && QCString(m.key().context()) == context ); t << "<context"; if ( contextIsUtf8 ) t << " encoding=\"UTF-8\""; t << ">\n"; t << " <name>" << evilBytes( context, contextIsUtf8 ) << "</name>\n"; if ( !comment.isEmpty() ) t << " <comment>" << evilBytes( comment, contextIsUtf8 ) << "</comment>\n"; for ( i = inv.begin(); i != inv.end(); ++i ) { // no need for such noise if ( (*i).type() == MetaTranslatorMessage::Obsolete && (*i).translation().isEmpty() ) continue; t << " <message"; if ( (*i).utf8() ) t << " encoding=\"UTF-8\""; t << ">\n" << " <source>" << evilBytes( (*i).sourceText(), (*i).utf8() ) << "</source>\n"; if ( !QCString((*i).comment()).isEmpty() ) t << " <comment>" << evilBytes( (*i).comment(), (*i).utf8() ) << "</comment>\n"; t << " <translation"; if ( (*i).type() == MetaTranslatorMessage::Unfinished ) t << " type=\"unfinished\""; else if ( (*i).type() == MetaTranslatorMessage::Obsolete ) t << " type=\"obsolete\""; t << ">" << protect( (*i).translation().utf8() ) << "</translation>\n"; t << " </message>\n"; } t << "</context>\n"; } t << "</TS>\n"; f.close(); return TRUE; } -bool MetaTranslator::release( const QString& filename, bool verbose ) const +bool MetaTranslator::release( const QString& filename, bool verbose, + QTranslator::SaveMode mode ) const { QTranslator tor( 0 ); int finished = 0; int unfinished = 0; int untranslated = 0; TMM::ConstIterator m; for ( m = mm.begin(); m != mm.end(); ++m ) { if ( m.key().type() != MetaTranslatorMessage::Obsolete ) { if ( m.key().translation().isEmpty() ) { untranslated++; } else { if ( m.key().type() == MetaTranslatorMessage::Unfinished ) unfinished++; else finished++; QCString context = m.key().context(); QCString sourceText = m.key().sourceText(); QCString comment = m.key().comment(); QString translation = m.key().translation(); /* Drop the comment in (context, sourceText, comment), unless (context, sourceText, "") already exists, or unless we already dropped the comment of (context, sourceText, comment0). */ if ( comment.isEmpty() || contains(context, sourceText, "") || !tor.findMessage(context, sourceText, "").translation() .isNull() ) { tor.insert( m.key() ); } else { tor.insert( QTranslatorMessage(context, sourceText, "", translation) ); } } } } - bool saved = tor.save( filename, QTranslator::Stripped ); + bool saved = tor.save( filename, mode ); if ( saved && verbose ) fprintf( stderr, " %d finished, %d unfinished and %d untranslated messages\n", finished, unfinished, untranslated ); return saved; } bool MetaTranslator::contains( const char *context, const char *sourceText, const char *comment ) const { return mm.find( MetaTranslatorMessage(context, sourceText, comment) ) != mm.end(); } void MetaTranslator::insert( const MetaTranslatorMessage& m ) { int pos = mm.count(); TMM::Iterator n = mm.find( m ); if ( n != mm.end() ) pos = *n; mm.replace( m, pos ); } void MetaTranslator::stripObsoleteMessages() { TMM newmm; TMM::Iterator m = mm.begin(); while ( m != mm.end() ) { if ( m.key().type() != MetaTranslatorMessage::Obsolete ) newmm.insert( m.key(), *m ); ++m; } mm = newmm; } void MetaTranslator::stripEmptyContexts() { TMM newmm; TMM::Iterator m = mm.begin(); while ( m != mm.end() ) { - if ( QCString(m.key().sourceText()).isEmpty() ) { + if ( QCString(m.key().sourceText()) == ContextComment ) { TMM::Iterator n = m; ++n; // the context comment is followed by other messages if ( n != newmm.end() && qstrcmp(m.key().context(), n.key().context()) == 0 ) newmm.insert( m.key(), *m ); } else { newmm.insert( m.key(), *m ); } ++m; } mm = newmm; } void MetaTranslator::setCodec( const char *name ) { const int latin1 = 4; codecName = name; codec = QTextCodec::codecForName( name ); if ( codec == 0 || codec->mibEnum() == latin1 ) codec = 0; } QString MetaTranslator::toUnicode( const char *str, bool utf8 ) const { if ( utf8 ) return QString::fromUtf8( str ); else if ( codec == 0 ) return QString( str ); else return codec->toUnicode( str ); } QValueList<MetaTranslatorMessage> MetaTranslator::messages() const { int n = mm.count(); TMM::ConstIterator *t = new TMM::ConstIterator[n + 1]; TMM::ConstIterator m; for ( m = mm.begin(); m != mm.end(); ++m ) t[*m] = m; QValueList<MetaTranslatorMessage> val; for ( int i = 0; i < n; i++ ) val.append( t[i].key() ); delete[] t; return val; } QValueList<MetaTranslatorMessage> MetaTranslator::translatedMessages() const { QValueList<MetaTranslatorMessage> val; TMM::ConstIterator m; for ( m = mm.begin(); m != mm.end(); ++m ) { if ( m.key().type() == MetaTranslatorMessage::Finished ) val.append( m.key() ); } return val; } diff --git a/development/translation/shared/metatranslator.h b/development/translation/shared/metatranslator.h index d35b202..a13f462 100644 --- a/development/translation/shared/metatranslator.h +++ b/development/translation/shared/metatranslator.h @@ -1,99 +1,113 @@ /********************************************************************** ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** ** This file is part of Qt Linguist. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #ifndef METATRANSLATOR_H #define METATRANSLATOR_H #include <qmap.h> #include <qstring.h> #include <qtranslator.h> #include <qvaluelist.h> class QTextCodec; class MetaTranslatorMessage : public QTranslatorMessage { public: enum Type { Unfinished, Finished, Obsolete }; MetaTranslatorMessage(); MetaTranslatorMessage( const char *context, const char *sourceText, const char *comment, const QString& translation = QString::null, bool utf8 = FALSE, Type type = Unfinished ); MetaTranslatorMessage( const MetaTranslatorMessage& m ); MetaTranslatorMessage& operator=( const MetaTranslatorMessage& m ); void setType( Type nt ) { ty = nt; } Type type() const { return ty; } bool utf8() const { return utfeight; } bool operator==( const MetaTranslatorMessage& m ) const; bool operator!=( const MetaTranslatorMessage& m ) const { return !operator==( m ); } bool operator<( const MetaTranslatorMessage& m ) const; bool operator<=( const MetaTranslatorMessage& m ) { return !operator>( m ); } bool operator>( const MetaTranslatorMessage& m ) const { return this->operator<( m ); } bool operator>=( const MetaTranslatorMessage& m ) const { return !operator<( m ); } private: bool utfeight; Type ty; }; class MetaTranslator { public: MetaTranslator(); MetaTranslator( const MetaTranslator& tor ); MetaTranslator& operator=( const MetaTranslator& tor ); + void clear(); bool load( const QString& filename ); bool save( const QString& filename ) const; - bool release( const QString& filename, bool verbose = FALSE ) const; + bool release( const QString& filename, bool verbose = FALSE, + QTranslator::SaveMode mode = QTranslator::Stripped ) const; bool contains( const char *context, const char *sourceText, const char *comment ) const; void insert( const MetaTranslatorMessage& m ); void stripObsoleteMessages(); void stripEmptyContexts(); void setCodec( const char *name ); QString toUnicode( const char *str, bool utf8 ) const; QValueList<MetaTranslatorMessage> messages() const; QValueList<MetaTranslatorMessage> translatedMessages() const; private: typedef QMap<MetaTranslatorMessage, int> TMM; typedef QMap<int, MetaTranslatorMessage> TMMInv; TMM mm; QCString codecName; QTextCodec *codec; }; +/* + This is a quick hack. The proper way to handle this would be + to extend MetaTranslator's interface. +*/ +#define ContextComment "QT_LINGUIST_INTERNAL_CONTEXT_COMMENT" + #endif diff --git a/development/translation/shared/opie.h b/development/translation/shared/opie.h index 4646bb0..28b2a61 100644 --- a/development/translation/shared/opie.h +++ b/development/translation/shared/opie.h @@ -1,21 +1,21 @@ #ifndef OPIE_H #define OPIE_H #include <qstring.h> #include <qstringlist.h> class OPIE { public: static OPIE* self(); /** get the list of languages */ QStringList languageList(const QString& opiedir = QString::null)const; - QString opieDir(const QString& opieDir)const; + QString opieDir(const QString& opieDir = QString::null)const; private: OPIE(); ~OPIE(); static OPIE* m_self; }; #endif diff --git a/development/translation/shared/proparser.cpp b/development/translation/shared/proparser.cpp index 21d2f86..f616c5a 100644 --- a/development/translation/shared/proparser.cpp +++ b/development/translation/shared/proparser.cpp @@ -1,87 +1,174 @@ /********************************************************************** ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** ** This file is part of Qt Linguist. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #include "proparser.h" +#include <qfile.h> #include <qregexp.h> #include <qstringlist.h> +#include <qtextstream.h> + +#ifdef Q_OS_UNIX +#include <unistd.h> +#endif + +#ifdef Q_OS_WIN32 +#define QT_POPEN _popen +#else +#define QT_POPEN popen +#endif + +QString loadFile( const QString &fileName ) +{ + QFile file( fileName ); + if ( !file.open(IO_ReadOnly) ) { + fprintf( stderr, "error: Cannot load '%s': %s\n", + file.name().latin1(), + file.errorString().latin1() ); + return QString(); + } + + QTextStream in( &file ); + return in.read(); +} -QMap<QString, QString> proFileTagMap( const QString& text ) +QMap<QString, QString> proFileTagMap( const QString& text, const QString& opieDir ) { QString t = text; /* + Process include() commands. + */ + QRegExp callToInclude("include\\s*\\(\\s*([^()\\s]+)\\s*\\)"); + int i = 0; + while ( (i = callToInclude.search(t, i)) != -1 ) { + QString after = loadFile( callToInclude.cap(1) ); + t.replace( i, callToInclude.matchedLength(), after ); + i += after.length(); + } + + /* Strip comments, merge lines ending with backslash, add spaces around '=' and '+=', replace '\n' with ';', and simplify white spaces. */ - t.replace( QRegExp(QString("#[^\n]$")), QString(" ") ); - t.replace( QRegExp(QString("\\\\\\s*\n")), QString(" ") ); + t.replace( QRegExp(QString("#[^\n]*\n")), QString(" ") ); + t.replace( QRegExp(QString("\\\\[^\n\\S]*\n")), QString(" ") ); t.replace( "=", QString(" = ") ); t.replace( "+ =", QString(" += ") ); t.replace( "\n", QString(";") ); t = t.simplifyWhiteSpace(); + /* + Populate tagMap with 'key = value' entries. + */ QMap<QString, QString> tagMap; - QStringList lines = QStringList::split( QChar(';'), t ); QStringList::Iterator line; for ( line = lines.begin(); line != lines.end(); ++line ) { QStringList toks = QStringList::split( QChar(' '), *line ); - if ( toks.count() >= 3 && + if ( toks.count() >= 3 && (toks[1] == QString("=") || toks[1] == QString("+=")) ) { QString tag = toks.first(); int k = tag.findRev( QChar(':') ); // as in 'unix:' if ( k != -1 ) tag = tag.mid( k + 1 ); toks.remove( toks.begin() ); QString action = toks.first(); toks.remove( toks.begin() ); if ( tagMap.contains(tag) ) { if ( action == QString("=") ) tagMap.replace( tag, toks.join(QChar(' ')) ); else tagMap[tag] += QChar( ' ' ) + toks.join( QChar(' ') ); } else { tagMap[tag] = toks.join( QChar(' ') ); } } } - QRegExp var( "\\$\\$[a-zA-Z0-9_]+" ); + /* + Expand $$variables within the 'value' part of a 'key = value' + pair. + */ + QRegExp var( "\\$\\$[({]?([a-zA-Z0-9_]+)[)}]?" ); QMap<QString, QString>::Iterator it; for ( it = tagMap.begin(); it != tagMap.end(); ++it ) { int i = 0; - - while ( (i = var.search(it.data(), i)) != -1 ) { + while ( (i = var.search((*it), i)) != -1 ) { int len = var.matchedLength(); - QString invocation = (*it).mid( i + 2, len - 2 ); + QString invocation = var.cap(1); QString after; - if ( tagMap.contains(invocation) ) - after = tagMap[invocation]; - (*it).replace( i, len, after ); + + if ( invocation == "system" ) { + // skip system(); it will be handled in the next pass + ++i; + } else if ( invocation == "OPIEDIR") { + (*it).replace( i, len, opieDir ); + }else { + if ( tagMap.contains(invocation) ) + after = tagMap[invocation]; + (*it).replace( i, len, after ); + } + } + } + + /* + Execute system() calls. + */ + QRegExp callToSystem( "\\$\\$system\\s*\\(([^()]*)\\)" ); + for ( it = tagMap.begin(); it != tagMap.end(); ++it ) { + int i = 0; + while ( (i = callToSystem.search((*it), i)) != -1 ) { + /* + This code is stolen from qmake's project.cpp file. + Ideally we would use the same parser, so we wouldn't + have this code duplication. + */ + QString after; + char buff[256]; + FILE *proc = QT_POPEN( callToSystem.cap(1).latin1(), "r" ); + while ( proc && !feof(proc) ) { + int read_in = fread( buff, 1, 255, proc ); + if ( !read_in ) + break; + for ( int i = 0; i < read_in; i++ ) { + if ( buff[i] == '\n' || buff[i] == '\t' ) + buff[i] = ' '; + } + buff[read_in] = '\0'; + after += buff; + } + (*it).replace( i, callToSystem.matchedLength(), after ); i += after.length(); } } + return tagMap; } diff --git a/development/translation/shared/proparser.h b/development/translation/shared/proparser.h index 6a61d90..e5678d6 100644 --- a/development/translation/shared/proparser.h +++ b/development/translation/shared/proparser.h @@ -1,29 +1,35 @@ /********************************************************************** ** Copyright (C) 2000 Trolltech AS. All rights reserved. ** ** This file is part of Qt Linguist. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. +** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for +** information about Qt Commercial License Agreements. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** **********************************************************************/ #ifndef PROPARSER_H #define PROPARSER_H #include <qmap.h> #include <qstring.h> -QMap<QString, QString> proFileTagMap( const QString& text ); +QMap<QString, QString> proFileTagMap( const QString& text, const QString& = QString::null ); #endif |