Update of /cvsroot/wpdev/wolfpack/tools/translationupdate
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8183/tools/translationupdate
Added Files:
fetchtr.cpp fetchtrwp.cpp main.cpp merge.cpp
metatranslator.cpp metatranslator.h numberh.cpp proparser.cpp
proparser.h sametexth.cpp translationUpdate.pro
Log Message:
- few updated clilocs
- commited our own version of lupdate that parses XML and Python scripts
--- NEW FILE: translationUpdate.pro ---
TEMPLATE = app
CONFIG += qt warn_on console
HEADERS = metatranslator.h \
proparser.h
SOURCES = fetchtr.cpp \
main.cpp \
merge.cpp \
numberh.cpp \
sametexth.cpp \
metatranslator.cpp \
proparser.cpp
TARGET = translationUpdate
--- NEW FILE: main.cpp ---
/**********************************************************************
** 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 sa...@tr... for
** information about Qt Commercial License Agreements.
**
** Contact in...@tr... if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/
#include <metatranslator.h>
#include <proparser.h>
#include <qdir.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 fetchtrwp.cpp
extern void fetchtr_xml( const char* fileName, MetaTranslator* tor, const char* defaultContext, bool mustExist );
extern void fetchtr_py( 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 void printUsage()
{
fprintf( stderr, "Usage:\n"
" lupdate [options] project-file\n"
" lupdate [options] source-files -ts ts-files\n"
"Options:\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 void updateTsFiles( const MetaTranslator& fetchedTor, const QStringList& tsFileNames, const QString& codec, bool noObsolete, bool verbose )
{
QStringList::ConstIterator t = tsFileNames.begin();
while ( t != tsFileNames.end() )
{
MetaTranslator tor;
tor.load( *t );
if ( !codec.isEmpty() )
tor.setCodec( codec );
if ( verbose )
fprintf( stderr, "Updating '%s'...\n", ( *t ).latin1() );
merge( &tor, &fetchedTor, verbose );
if ( noObsolete )
tor.stripObsoleteMessages();
tor.stripEmptyContexts();
if ( !tor.save( *t ) )
fprintf( stderr, "lupdate error: Cannot save '%s': %s\n", ( *t ).latin1(), strerror( errno ) );
++t;
}
}
typedef void(*fetchFunctor)(const char*, MetaTranslator*, const char*, bool);
static void lookForDefinitions( const QString& dir, const QString& filter, MetaTranslator* tor, fetchFunctor fetchtr )
{
QDir d ( dir );
d.setFilter( QDir::Dirs | QDir::Files | QDir::Hidden | QDir::NoSymLinks );
d.setNameFilter( filter );
d.setSorting( QDir::Name );
d.setMatchAllDirs( true );
const QFileInfoList *list = d.entryInfoList();
QFileInfoListIterator it( *list );
QFileInfo *fi;
for ( ; ( fi = it.current() ) != 0; ++it )
{
// skip "." and ".." dir
if ( fi->isDir() && ( fi->fileName() == "." || fi->fileName() == ".." ) )
continue;
else if ( fi->isDir() )
lookForDefinitions( d.absPath() + "/" + fi->fileName(), filter, tor, fetchtr );
else
{
fetchtr( dir + "/" + fi->fileName(), tor, 0, true );
}
}
}
int main( int argc, char** argv )
{
QString defaultContext = "@default";
MetaTranslator fetchedTor;
QCString codec;
QStringList tsFileNames;
bool verbose = FALSE;
bool noObsolete = FALSE;
bool metSomething = FALSE;
int numFiles = 0;
bool standardSyntax = TRUE;
bool metTsFlag = FALSE;
int i;
for ( i = 1; i < argc; i++ )
{
if ( qstrcmp( argv[i], "-ts" ) == 0 )
standardSyntax = FALSE;
}
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", QT_VERSION_STR );
return 0;
}
else if ( qstrcmp( argv[i], "-ts" ) == 0 )
{
metTsFlag = TRUE;
continue;
}
numFiles++;
QString fullText;
if ( !metTsFlag )
{
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();
}
QString oldDir = QDir::currentDirPath();
QDir::setCurrent( QFileInfo( argv[i] ).dirPath() );
if ( standardSyntax )
{
fetchedTor = MetaTranslator();
codec.truncate( 0 );
tsFileNames.clear();
QMap<QString, QString> tagMap = proFileTagMap( fullText );
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" )
{
tsFileNames.append( *t );
metSomething = TRUE;
}
else if ( it.key() == "CODEC" || it.key() == "DEFAULTCODEC" )
{
codec = ( *t ).latin1();
}
else if ( it.key() == "WPDEFINITIONS" )
{
lookForDefinitions( *t, "*.xml", &fetchedTor, &fetchtr_xml );
}
else if ( it.key() == "WPSCRIPTS" )
{
lookForDefinitions( *t, "*.py", &fetchedTor, &fetchtr_py );
}
}
}
updateTsFiles( fetchedTor, tsFileNames, codec, noObsolete, verbose );
if ( !metSomething )
{
fprintf( stderr, "lupdate warning: File '%s' does not look like a"
" project file\n", argv[i] );
}
else if ( tsFileNames.isEmpty() )
{
fprintf( stderr, "lupdate warning: Met no 'TRANSLATIONS' entry in"
" project file '%s'\n", argv[i] );
}
}
else
{
if ( metTsFlag )
{
if ( QString( argv[i] ).lower().endsWith( ".ts" ) )
{
QFileInfo fi( argv[i] );
if ( !fi.exists() || fi.isWritable() )
{
tsFileNames.append( argv[i] );
}
else
{
fprintf( stderr, "lupdate warning: For some reason, I cannot"
" save '%s'\n", argv[i] );
}
}
else
{
fprintf( stderr, "lupdate error: File '%s' lacks .ts extension\n", argv[i] );
}
}
else
{
QFileInfo fi( argv[i] );
if ( QString( argv[i] ).lower().endsWith( ".ui" ) )
{
fetchtr_ui( fi.fileName(), &fetchedTor, defaultContext, TRUE );
fetchtr_cpp( QString( fi.fileName() ) + ".h", &fetchedTor, defaultContext, FALSE );
}
else if ( QString( argv[i] ).lower().endsWith( ".xml" ) )
{
fetchtr_xml( fi.fileName(), &fetchedTor, 0, TRUE );
}
else if ( QString( argv[i] ).lower().endsWith( ".py" ) )
{
fetchtr_py( fi.fileName(), &fetchedTor, 0, TRUE );
}
else
{
fetchtr_cpp( fi.fileName(), &fetchedTor, defaultContext, TRUE );
}
}
}
QDir::setCurrent( oldDir );
}
if ( !standardSyntax )
updateTsFiles( fetchedTor, tsFileNames, codec, noObsolete, verbose );
if ( numFiles == 0 )
{
printUsage();
return 1;
}
return 0;
}
--- NEW FILE: fetchtr.cpp ---
/**********************************************************************
** 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 sa...@tr... for
** information about Qt Commercial License Agreements.
**
** Contact in...@tr... 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 'O':
if ( strcmp ( yyIdent + 1, "BJECTDEF" ) == 0 )
{
return Tok_Q_OBJECT;
}
break;
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 '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 '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() )
{
yyBraceDepth = yySavedBraceDepth.top();
yyParenDepth = yySavedParenDepth.top();
}
}
else if ( yyCh == 'n' )
{
// endif
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();
}
while ( yyTok == Tok_Ident );
while ( yyTok == Tok_Gulbrandsen )
{
yyTok = getToken();
functionContext += "::";
functionContext += yyIdent;
yyTok = getToken();
}
if ( yyTok == Tok_Colon )
{
missing_Q_OBJECT = TRUE;
}
else
{
functionContext = defaultContext;
}
}
break;
case Tok_namespace:
yyTok = getToken();
if ( yyTok == Tok_Ident )
{
QCString ns = yyIdent;
yyTok = getToken();
if ( yyTok == Tok_LeftBrace && yyBraceDepth == ( int ) namespaces.count() + 1 )
namespaces.append( QString( ns ) );
}
break;
case Tok_tr:
case Tok_trUtf8:
utf8 = ( yyTok == Tok_trUtf8 );
yyTok = getToken();
if ( match( Tok_LeftParen ) && matchString( &text ) )
{
com = "";
if ( match( Tok_RightParen ) || ( match( Tok_Comma ) && matchString( &com ) && match( Tok_RightParen ) ) )
{
if ( prefix.isNull() )
{
context = functionContext;
if ( !namespaces.isEmpty() )
context.prepend( ( namespaces.join( QString( "::" ) ) + QString( "::" ) ).latin1() );
}
else
{
context = prefix;
}
prefix = ( const char * ) 0;
if ( qualifiedContexts.contains( context ) )
context = qualifiedContexts[context];
tor->insert( MetaTranslatorMessage( context, text, com, QString::null, utf8 ) );
if ( lacks_Q_OBJECT.contains( context ) )
{
qWarning( "%s:%d: Class '%s' lacks OBJECTDEF macro", ( const char * ) yyFileName, yyLineNo, ( const char * ) context );
lacks_Q_OBJECT.remove( context );
}
else
{
needs_Q_OBJECT.insert( context, 0 );
}
}
}
break;
case Tok_translate:
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 OBJECTDEF 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:%d: Unbalanced braces in C++ code (or abuse of the C++"
" preprocessor)\n", ( const char * ) yyFileName, yyBraceLineNo );
else if ( yyParenDepth != 0 )
fprintf( stderr, "%s:%d: Unbalanced parentheses in C++ code (or abuse of the C++"
" preprocessor)\n", ( 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
{
flush();
}
return TRUE;
}
bool UiHandler::characters( const QString& ch )
{
accum += ch;
return TRUE;
}
bool UiHandler::fatalError( const QXmlParseException& exception )
{
QString msg;
msg.sprintf( "Parse error at line %d, column %d (%s).", exception.lineNumber(), exception.columnNumber(), exception.message().latin1() );
fprintf( stderr, "XML error: %s\n", msg.latin1() );
return FALSE;
}
void UiHandler::flush()
{
if ( !context.isEmpty() && !source.isEmpty() )
tor->insert( MetaTranslatorMessage( context.utf8(), source.utf8(), comment.utf8(), QString::null, TRUE ) );
source.truncate( 0 );
comment.truncate( 0 );
}
void fetchtr_ui( const char* fileName, MetaTranslator* tor, const char * /* defaultContext */, bool mustExist )
{
QFile f( fileName );
if ( !f.open( IO_ReadOnly ) )
{
if ( mustExist )
fprintf( stderr, "lupdate error: cannot open UI file '%s': %s\n", fileName, strerror( errno ) );
return;
}
QTextStream t( &f );
QXmlInputSource in( t );
QXmlSimpleReader reader;
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 UiHandler( tor, fileName );
reader.setContentHandler( hand );
reader.setErrorHandler( hand );
if ( !reader.parse( in ) )
fprintf( stderr, "%s: Parse error in UI file\n", fileName );
reader.setContentHandler( 0 );
reader.setErrorHandler( 0 );
delete hand;
f.close();
}
--- NEW FILE: fetchtrwp.cpp ---
#include <metatranslator.h>
#include <qfile.h>
#include <qregexp.h>
#include <qstring.h>
#include <qtextstream.h>
#include <qvaluestack.h>
#include <qregexp.h>
#include <qxml.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
void fetchtr_py( const char* fileName, MetaTranslator* tor, const char* /*defaultContext*/, bool mustExist )
{
static QRegExp matchtr("\\W(tr)\\s{0,1}\\(");
static QRegExp stringDelimiter("(\"?|\'?)");
static QString context("@pythonscript");
QFile f( fileName );
if ( !f.open( IO_ReadOnly ) )
{
if ( mustExist )
fprintf( stderr, "translationUpdate error: Cannot open Python source file '%s': %s\n", fileName, f.errorString() );
return;
}
QString content = QString( f.readAll() );
content.replace( QRegExp("#[^\n]*"), "" );; // remove single line comments
int pos = 0;
while ( ( pos = matchtr.search(content, pos) ) != -1 )
{
QString message = content.mid(pos + matchtr.matchedLength());
int start = 0, end = 0;
if ( ( start = stringDelimiter.search(message) ) == -1 )
{
pos += matchtr.matchedLength();
continue;
}
QString startDelimiter = message.mid( start, stringDelimiter.matchedLength() );
end = message.find( startDelimiter, start + startDelimiter.length() );
if ( end == -1 )
{
fprintf( stderr, "translationUpdate error: Open string in tr() '%s': %i\n", fileName, pos );
return;
}
QString source = message.mid( start + startDelimiter.length(), end - startDelimiter.length() );
if ( !source.isEmpty() )
tor->insert( MetaTranslatorMessage( context.utf8(), source.utf8(), QString(fileName).utf8(), QString::null, TRUE ) );
pos += QMAX( end, matchtr.matchedLength() );
}
}
class DefinitionHandler : public QXmlDefaultHandler
{
public:
DefinitionHandler( MetaTranslator* translator, const char* fileName ) : tor( translator ),
fname( fileName ), comment( "" )
{
context = "@definitions";
}
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 DefinitionHandler::startElement( const QString& /* namespaceURI */, const QString& /* localName */, const QString& qName, const QXmlAttributes& /* atts */ )
{
if ( qName == QString( "title" ) )
{
flush();
}
accum.truncate( 0 );
return TRUE;
}
bool DefinitionHandler::endElement( const QString& /* namespaceURI */, const QString& /* localName */, const QString& qName )
{
accum.replace( QRegExp( QString( "\r\n" ) ), "\n" );
if ( qName == QString( "title" ) )
{
source = accum;
}
else
{
flush();
}
return TRUE;
}
bool DefinitionHandler::characters( const QString& ch )
{
accum += ch;
return TRUE;
}
bool DefinitionHandler::fatalError( const QXmlParseException& exception )
{
QString msg;
msg.sprintf( "Parse error at line %d, column %d (%s).", exception.lineNumber(), exception.columnNumber(), exception.message().latin1() );
fprintf( stderr, "XML error: %s\n", msg.latin1() );
return FALSE;
}
void DefinitionHandler::flush()
{
if ( !context.isEmpty() && !source.isEmpty() )
tor->insert( MetaTranslatorMessage( context.utf8(), source.utf8(), comment.utf8(), QString::null, TRUE ) );
source.truncate( 0 );
comment.truncate( 0 );
}
void fetchtr_xml( const char* fileName, MetaTranslator* tor, const char* /* defaultContext */, bool mustExist )
{
QFile f( fileName );
if ( !f.open( IO_ReadOnly ) )
{
if ( mustExist )
fprintf( stderr, "translationupdate error: cannot open XML file '%s': %s\n", fileName, strerror( errno ) );
return;
}
QTextStream t( &f );
QXmlInputSource in( t );
QXmlSimpleReader reader;
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 DefinitionHandler( tor, fileName );
reader.setContentHandler( hand );
reader.setErrorHandler( hand );
if ( !reader.parse( in ) )
fprintf( stderr, "%s: Parse error in XML file\n", fileName );
reader.setContentHandler( 0 );
reader.setErrorHandler( 0 );
delete hand;
f.close();
}
--- NEW FILE: proparser.cpp ---
/**********************************************************************
** 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 sa...@tr... for
** information about Qt Commercial License Agreements.
**
** Contact in...@tr... if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/
#include "proparser.h"
#include <qdir.h>
#include <qfile.h>
#include <qfileinfo.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 )
{
QString t = text;
QMap<QString, QString> tagMap;
bool stillProcess = true; // If include() has a $$tag then we need to reprocess
while ( stillProcess )
{
/*
Strip any commments before we try to include. We
still need to do it after we include to make sure the
included file does not have comments
*/
t.replace( QRegExp( QString( "#[^\n]*\n" ) ), QString( " " ) );
/*
Process include() commands.
$$PWD is a special case so we have to change it while
we know where the included file is.
*/
QRegExp callToInclude( "include\\s*\\(\\s*([^()\\s]+)\\s*\\)" );
int i = 0;
while ( ( i = callToInclude.search( t, i ) ) != -1 )
{
bool doneWithVar = false;
QString fileName = callToInclude.cap( 1 );
QString after = fileName.replace( "$$PWD", QDir::currentDirPath() );
if ( !tagMap.isEmpty() && after.contains( "$$" ) )
{
QRegExp var( "\\$\\$[({]?([a-zA-Z0-9_]+)[)}]?" );
int ii = 0;
while ( ( ii = after.find( var, ii ) ) != -1 )
{
if ( tagMap.contains( var.cap( 1 ) ) )
{
after.replace( ii, var.cap( 0 ).length(), tagMap[var.cap( 1 )] );
}
else
{
// Couldn't find it
doneWithVar = true;
break;
}
}
}
if ( doneWithVar || !after.contains( "$$" ) )
{
after = loadFile( after );
QFileInfo fi( callToInclude.cap( 1 ) );
after.replace( "$$PWD", fi.dirPath() );
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]*\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.
*/
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 && ( 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( ' ' ) );
}
}
}
/*
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 ), i ) ) != -1 )
{
int len = var.matchedLength();
QString invocation = var.cap( 1 );
QString after;
if ( invocation == "system" )
{
// skip system(); it will be handled in the next pass
++i;
}
else
{
if ( tagMap.contains( invocation ) )
after = tagMap[invocation];
else if ( invocation.lower() == "pwd" )
after = QDir::currentDirPath();
( *it ).replace( i, len, after );
i += after.length();
}
}
}
/*
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();
}
}
stillProcess = callToInclude.search( t ) != -1;
}
return tagMap;
}
--- NEW FILE: sametexth.cpp ---
/**********************************************************************
** 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 sa...@tr... for
** information about Qt Commercial License Agreements.
**
** Contact in...@tr... 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" );
}
--- NEW FILE: metatranslator.cpp ---
/**********************************************************************
** 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 sa...@tr... for
** information about Qt Commercial License Agreements.
**
** Contact in...@tr... 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(), ContextComment, accum.utf8(), QString::null, TRUE, MetaTranslatorMessage::Unfinished ) );
else
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()
{
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;
}
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;
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();
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() ) == 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, 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, 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() ) == 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;
}
--- NEW FILE: merge.cpp ---
/**********************************************************************
** 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
** WAR...
[truncated message content] |