summaryrefslogtreecommitdiff
Side-by-side diff
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--development/translation/opie-lrelease/lrelease.pro16
-rw-r--r--development/translation/opie-lrelease/main.cpp150
-rw-r--r--development/translation/opie-lupdate/fetchtr.cpp784
-rwxr-xr-xdevelopment/translation/opie-lupdate/lupdatebin0 -> 128093 bytes
-rw-r--r--development/translation/opie-lupdate/main.cpp215
-rw-r--r--development/translation/opie-lupdate/merge.cpp115
-rw-r--r--development/translation/opie-lupdate/numberh.cpp235
-rw-r--r--development/translation/opie-lupdate/opie-lupdate.pro21
-rw-r--r--development/translation/opie-lupdate/sametexth.cpp84
-rw-r--r--development/translation/shared/metatranslator.cpp586
-rw-r--r--development/translation/shared/metatranslator.h99
-rw-r--r--development/translation/shared/opie.cpp40
-rw-r--r--development/translation/shared/opie.h21
-rw-r--r--development/translation/shared/proparser.cpp87
-rw-r--r--development/translation/shared/proparser.h29
15 files changed, 2482 insertions, 0 deletions
diff --git a/development/translation/opie-lrelease/lrelease.pro b/development/translation/opie-lrelease/lrelease.pro
new file mode 100644
index 0000000..fa285cb
--- a/dev/null
+++ b/development/translation/opie-lrelease/lrelease.pro
@@ -0,0 +1,16 @@
+TEMPLATE = app
+CONFIG += qt warn_on console
+HEADERS = ../shared/metatranslator.h \
+ ../shared/proparser.h \
+ ../shared/opie.h
+SOURCES = main.cpp \
+ ../shared/metatranslator.cpp \
+ ../shared/proparser.cpp \
+ ../shared/opie.cpp
+
+DEFINES += QT_INTERNAL_XML
+
+TARGET = opie-lrelease
+INCLUDEPATH += ../shared
+#DESTDIR = ../../../bin
+
diff --git a/development/translation/opie-lrelease/main.cpp b/development/translation/opie-lrelease/main.cpp
new file mode 100644
index 0000000..6008c4e
--- a/dev/null
+++ b/development/translation/opie-lrelease/main.cpp
@@ -0,0 +1,150 @@
+/**********************************************************************
+** 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.
+**
+** 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 <qregexp.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+
+#include <errno.h>
+
+typedef QValueList<MetaTranslatorMessage> TML;
+
+static void printUsage()
+{
+ fprintf( stderr, "Usage:\n"
+ " lrelease [options] project-file\n"
+ " lrelease [options] ts-files\n"
+ "Options:\n"
+ " -opie OPIE dir overrides $OPIEDIR\n"
+ " -help Display this information and exit\n"
+ " -verbose\n"
+ " Explain what is being done\n"
+ " -version\n"
+ " Display the version of lrelease and exit\n" );
+}
+static void releaseQmFile( const QString& tsFileName, bool verbose )
+{
+ MetaTranslator tor;
+ QString qmFileName = tsFileName;
+ qmFileName.replace( QRegExp("\\.ts$"), "" );
+ qmFileName += ".qm";
+
+ if ( tor.load(tsFileName) ) {
+ if ( verbose )
+ fprintf( stderr, "Updating '%s'...\n", qmFileName.latin1() );
+ if ( !tor.release(qmFileName, verbose) )
+ fprintf( stderr,
+ "lrelease warning: For some reason, I cannot save '%s'\n",
+ qmFileName.latin1() );
+ } else {
+ fprintf( stderr,
+ "lrelease warning: For some reason, I cannot load '%s'\n",
+ tsFileName.latin1() );
+ }
+}
+static void metaQmFile( const QString &opiedir,
+ const QStringList& lang,
+ const QString& basename,
+ bool isLib, bool verb ) {
+ QString target = basename + ".ts";
+ if ( isLib ) target.prepend("lib");
+
+ for ( QStringList::ConstIterator it = lang.begin(); it != lang.end();
+ ++it ) {
+ QString fileName = opiedir + "/i18n/" + (*it) + "/" + target;
+ qWarning("Target is %s", fileName.latin1() );
+ }
+}
+int main( int argc, char **argv )
+{
+ bool verbose = FALSE;
+ bool metTranslations = FALSE;
+ int numFiles = 0;
+ QString opiedir;
+ QStringList languageList = OPIE::self()->languageList( opiedir );
+
+ for ( int i = 1; i < argc; i++ ) {
+ if ( qstrcmp(argv[i], "-help") == 0 ) {
+ printUsage();
+ return 0;
+ } else if ( qstrcmp(argv[i], "-verbose") == 0 ) {
+ verbose = TRUE;
+ continue;
+ } else if ( qstrcmp(argv[i], "-version") == 0 ) {
+ fprintf( stderr, "lrelease version %s\n", QT_VERSION_STR );
+ return 0;
+ } else if ( qstrcmp(argv[i], "-opie") == 0 ) {
+ if ( i+1 < argc ) {
+ opiedir = argv[i+1];
+ languageList = OPIE::self()->languageList(opiedir);
+ }
+ }
+
+ numFiles++;
+ QFile f( argv[i] );
+ if ( !f.open(IO_ReadOnly) ) {
+ fprintf( stderr,
+ "lrelease error: Cannot open file '%s': %s\n", argv[i],
+ strerror(errno) );
+ return 1;
+ }
+
+ QTextStream t( &f );
+ QString fullText = t.read();
+ f.close();
+
+ if ( fullText.find(QString("<!DOCTYPE TS>")) >= 0 ) {
+ releaseQmFile( argv[i], verbose );
+ } else {
+ QString target;
+ bool isLib = FALSE;
+ 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() == "TARGET" ) {
+ target = *t;
+ }else if ( it.key() == "TEMPLATE" ) {
+ if ( (*t).stripWhiteSpace().lower() == "lib" )
+ isLib = TRUE;
+ }
+ }
+ }
+ metaQmFile( OPIE::self()->opieDir(opiedir),
+ languageList, target, isLib, verbose );
+ }
+ }
+
+ if ( numFiles == 0 ) {
+ printUsage();
+ return 1;
+ }
+ return 0;
+}
diff --git a/development/translation/opie-lupdate/fetchtr.cpp b/development/translation/opie-lupdate/fetchtr.cpp
new file mode 100644
index 0000000..eb25555
--- a/dev/null
+++ b/development/translation/opie-lupdate/fetchtr.cpp
@@ -0,0 +1,784 @@
+/**********************************************************************
+** 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.
+**
+** 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 <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 int yyBraceDepth;
+static int yyParenDepth;
+static int yyLineNo;
+static int yyCurLineNo;
+
+// 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();
+ yyBraceDepth = 0;
+ yyParenDepth = 0;
+ yyCurLineNo = 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 '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 );
+ }
+ break;
+ case 'e':
+ yyCh = getChar();
+ if ( yyCh == 'l' ) {
+ // elif, else
+ if ( !yySavedBraceDepth.isEmpty() )
+ yyBraceDepth = yySavedBraceDepth.top();
+ } else if ( yyCh == 'n' ) {
+ // endif
+ if ( !yySavedBraceDepth.isEmpty() )
+ yySavedBraceDepth.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 '{':
+ yyBraceDepth++;
+ yyCh = getChar();
+ return Tok_LeftBrace;
+ case '}':
+ yyBraceDepth--;
+ yyCh = getChar();
+ return Tok_RightBrace;
+ case '(':
+ yyParenDepth++;
+ yyCh = getChar();
+ return Tok_LeftParen;
+ case ')':
+ 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 Q_OBJECT 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 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++"
+ " preprocessor)\n",
+ (const char *) yyFileName );
+ if ( yyParenDepth != 0 )
+ fprintf( stderr,
+ "%s: Unbalanced parentheses in C++ code (or abuse of the C++"
+ " preprocessor)\n",
+ (const char *) yyFileName );
+}
+
+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();
+}
diff --git a/development/translation/opie-lupdate/lupdate b/development/translation/opie-lupdate/lupdate
new file mode 100755
index 0000000..0e1604d
--- a/dev/null
+++ b/development/translation/opie-lupdate/lupdate
Binary files differ
diff --git a/development/translation/opie-lupdate/main.cpp b/development/translation/opie-lupdate/main.cpp
new file mode 100644
index 0000000..ce65e7a
--- a/dev/null
+++ b/development/translation/opie-lupdate/main.cpp
@@ -0,0 +1,215 @@
+/**********************************************************************
+** 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 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>::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
new file mode 100644
index 0000000..a96104e
--- a/dev/null
+++ b/development/translation/opie-lupdate/merge.cpp
@@ -0,0 +1,115 @@
+/**********************************************************************
+** 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.
+**
+** 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>
+
+// 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 );
+
+ if ( verbose )
+ fprintf( stderr, " %d known, %d new and %d obsoleted messages\n", known,
+ neww, obsoleted );
+}
diff --git a/development/translation/opie-lupdate/numberh.cpp b/development/translation/opie-lupdate/numberh.cpp
new file mode 100644
index 0000000..f7b7bf8
--- a/dev/null
+++ b/development/translation/opie-lupdate/numberh.cpp
@@ -0,0 +1,235 @@
+/**********************************************************************
+** 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.
+**
+** 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 <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 );
+}
+
+static int numberLength( const char *s )
+{
+ int i = 0;
+
+ if ( isdigit(s[0]) ) {
+ do {
+ i++;
+ } while ( isdigit(s[i]) ||
+ (isDigitFriendly(s[i]) &&
+ (isdigit(s[i + 1]) ||
+ (isDigitFriendly(s[i + 1]) && isdigit(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 );
+ } else if ( !(*it).translation().isEmpty() ) {
+ translated.insert( zeroKey((*it).sourceText()), *it );
+ }
+ }
+
+ for ( u = untranslated.begin(); u != untranslated.end(); ++u ) {
+ t = translated.find( u.key() );
+ 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()) );
+ 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/opie-lupdate.pro b/development/translation/opie-lupdate/opie-lupdate.pro
new file mode 100644
index 0000000..ea51c8b
--- a/dev/null
+++ b/development/translation/opie-lupdate/opie-lupdate.pro
@@ -0,0 +1,21 @@
+TEMPLATE = app
+CONFIG += qt warn_on console
+HEADERS = ../shared/metatranslator.h \
+ ../shared/proparser.h \
+ ../shared/opie.h
+
+SOURCES = fetchtr.cpp \
+ main.cpp \
+ merge.cpp \
+ numberh.cpp \
+ sametexth.cpp \
+ ../shared/metatranslator.cpp \
+ ../shared/proparser.cpp \
+ ../shared/opie.cpp
+
+DEFINES += QT_INTERNAL_XML
+
+TARGET = opie-lupdate
+INCLUDEPATH += ../shared
+#DESTDIR =
+
diff --git a/development/translation/opie-lupdate/sametexth.cpp b/development/translation/opie-lupdate/sametexth.cpp
new file mode 100644
index 0000000..574cfd5
--- a/dev/null
+++ b/development/translation/opie-lupdate/sametexth.cpp
@@ -0,0 +1,84 @@
+/**********************************************************************
+** 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.
+**
+** 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 <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
new file mode 100644
index 0000000..a01e1eb
--- a/dev/null
+++ b/development/translation/shared/metatranslator.cpp
@@ -0,0 +1,586 @@
+/**********************************************************************
+** 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.
+**
+** 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 <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(), "",
+ accum.utf8(), QString::null, TRUE,
+ MetaTranslatorMessage::Unfinished) );
+ else
+ tor->insert( MetaTranslatorMessage(context.ascii(), "",
+ 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( "&quot;" );
+ break;
+ case '&':
+ result += QString( "&amp;" );
+ break;
+ case '>':
+ result += QString( "&gt;" );
+ break;
+ case '<':
+ result += QString( "&lt;" );
+ break;
+ case '\'':
+ result += QString( "&apos;" );
+ 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 )
+{
+}
+
+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 )
+{
+ mm.clear();
+
+ 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 ( 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
+{
+ 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 );
+ 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() ) {
+ 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
new file mode 100644
index 0000000..d35b202
--- a/dev/null
+++ b/development/translation/shared/metatranslator.h
@@ -0,0 +1,99 @@
+/**********************************************************************
+** 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.
+**
+** 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.
+**
+**********************************************************************/
+
+#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 );
+
+ bool load( const QString& filename );
+ bool save( const QString& filename ) const;
+ bool release( const QString& filename, bool verbose = FALSE ) 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;
+};
+
+#endif
diff --git a/development/translation/shared/opie.cpp b/development/translation/shared/opie.cpp
new file mode 100644
index 0000000..c5c72d1
--- a/dev/null
+++ b/development/translation/shared/opie.cpp
@@ -0,0 +1,40 @@
+#include <stdlib.h>
+
+#include <qdir.h>
+
+#include "opie.h"
+
+OPIE* OPIE::m_self = 0;
+
+
+OPIE::OPIE() {
+}
+OPIE::~OPIE() {
+}
+OPIE* OPIE::self() {
+ if (!m_self ) m_self = new OPIE;
+ return m_self;
+}
+QStringList OPIE::languageList( const QString& _opieDir )const {
+ QString opieDi = opieDir( _opieDir );
+
+ QStringList langs;
+ QDir dir( opieDi + "/i18n/");
+ if (!dir.exists() ) return langs;
+ langs = dir.entryList( QDir::Dirs );
+
+ langs.remove("CVS"); // hey this no language
+ langs.remove("unmaintained"); // remove this one too
+ langs.remove(".");
+ langs.remove("..");
+
+
+
+ return langs;
+}
+QString OPIE::opieDir( const QString& _opieDir ) const{
+ if (!_opieDir.isEmpty() ) return _opieDir;
+ char* dir = ::getenv("OPIEDIR");
+ if (!dir ) return QString::null;
+ return QString::fromLatin1(dir);
+}
diff --git a/development/translation/shared/opie.h b/development/translation/shared/opie.h
new file mode 100644
index 0000000..4646bb0
--- a/dev/null
+++ b/development/translation/shared/opie.h
@@ -0,0 +1,21 @@
+#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;
+
+private:
+ OPIE();
+ ~OPIE();
+ static OPIE* m_self;
+
+};
+
+#endif
diff --git a/development/translation/shared/proparser.cpp b/development/translation/shared/proparser.cpp
new file mode 100644
index 0000000..21d2f86
--- a/dev/null
+++ b/development/translation/shared/proparser.cpp
@@ -0,0 +1,87 @@
+/**********************************************************************
+** 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.
+**
+** 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 "proparser.h"
+
+#include <qregexp.h>
+#include <qstringlist.h>
+
+QMap<QString, QString> proFileTagMap( const QString& text )
+{
+ QString t = text;
+
+ /*
+ 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( "=", QString(" = ") );
+ t.replace( "+ =", QString(" += ") );
+ t.replace( "\n", QString(";") );
+ t = t.simplifyWhiteSpace();
+
+ 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 &&
+ (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_]+" );
+ QMap<QString, QString>::Iterator it;
+ for ( it = tagMap.begin(); it != tagMap.end(); ++it ) {
+ int i = 0;
+
+ while ( (i = var.search(it.data(), i)) != -1 ) {
+ int len = var.matchedLength();
+ QString invocation = (*it).mid( i + 2, len - 2 );
+ QString after;
+ if ( tagMap.contains(invocation) )
+ after = tagMap[invocation];
+ (*it).replace( i, len, after );
+ i += after.length();
+ }
+ }
+ return tagMap;
+}
diff --git a/development/translation/shared/proparser.h b/development/translation/shared/proparser.h
new file mode 100644
index 0000000..6a61d90
--- a/dev/null
+++ b/development/translation/shared/proparser.h
@@ -0,0 +1,29 @@
+/**********************************************************************
+** 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.
+**
+** 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.
+**
+**********************************************************************/
+
+#ifndef PROPARSER_H
+#define PROPARSER_H
+
+#include <qmap.h>
+#include <qstring.h>
+
+QMap<QString, QString> proFileTagMap( const QString& text );
+
+#endif