Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

metatranslator.cpp

Go to the documentation of this file.
00001 /**********************************************************************
00002 ** Copyright (C) 2000-2002 Trolltech AS.  All rights reserved.
00003 **
00004 ** This file is part of Qt Linguist.
00005 **
00006 ** This file may be distributed and/or modified under the terms of the
00007 ** GNU General Public License version 2 as published by the Free Software
00008 ** Foundation and appearing in the file LICENSE.GPL included in the
00009 ** packaging of this file.
00010 **
00011 ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
00012 ** licenses may use this file in accordance with the Qt Commercial License
00013 ** Agreement provided with the Software.
00014 **
00015 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00016 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00017 **
00018 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
00019 ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
00020 **   information about Qt Commercial License Agreements.
00021 **
00022 ** Contact info@trolltech.com if any conditions of this licensing are
00023 ** not clear to you.
00024 **
00025 **********************************************************************/
00026 
00027 #include "metatranslator.h"
00028 
00029 #include <qapplication.h>
00030 #include <qcstring.h>
00031 #include <qfile.h>
00032 #include <qmessagebox.h>
00033 #include <qtextcodec.h>
00034 #include <qtextstream.h>
00035 #include <qxml.h>
00036 
00037 static bool encodingIsUtf8( const QXmlAttributes& atts )
00038 {
00039     for ( int i = 0; i < atts.length(); i++ ) {
00040         // utf8="true" is a pre-3.0 syntax
00041         if ( atts.qName(i) == QString("utf8") ) {
00042             return ( atts.value(i) == QString("true") );
00043         } else if ( atts.qName(i) == QString("encoding") ) {
00044             return ( atts.value(i) == QString("UTF-8") );
00045         }
00046     }
00047     return FALSE;
00048 }
00049 
00050 class TsHandler : public QXmlDefaultHandler
00051 {
00052 public:
00053     TsHandler( MetaTranslator *translator )
00054         : tor( translator ), type( MetaTranslatorMessage::Finished ),
00055           inMessage( FALSE ), ferrorCount( 0 ), contextIsUtf8( FALSE ),
00056           messageIsUtf8( FALSE ) { }
00057 
00058     virtual bool startElement( const QString& namespaceURI,
00059                                const QString& localName, const QString& qName,
00060                                const QXmlAttributes& atts );
00061     virtual bool endElement( const QString& namespaceURI,
00062                              const QString& localName, const QString& qName );
00063     virtual bool characters( const QString& ch );
00064     virtual bool fatalError( const QXmlParseException& exception );
00065 
00066 private:
00067     MetaTranslator *tor;
00068     MetaTranslatorMessage::Type type;
00069     bool inMessage;
00070     QString context;
00071     QString source;
00072     QString comment;
00073     QString translation;
00074 
00075     QString accum;
00076     int ferrorCount;
00077     bool contextIsUtf8;
00078     bool messageIsUtf8;
00079 };
00080 
00081 bool TsHandler::startElement( const QString& /* namespaceURI */,
00082                               const QString& /* localName */,
00083                               const QString& qName,
00084                               const QXmlAttributes& atts )
00085 {
00086     if ( qName == QString("byte") ) {
00087         for ( int i = 0; i < atts.length(); i++ ) {
00088             if ( atts.qName(i) == QString("value") ) {
00089                 QString value = atts.value( i );
00090                 int base = 10;
00091                 if ( value.startsWith("x") ) {
00092                     base = 16;
00093                     value = value.mid( 1 );
00094                 }
00095                 int n = value.toUInt( 0, base );
00096                 if ( n != 0 )
00097                     accum += QChar( n );
00098             }
00099         }       
00100     } else {
00101         if ( qName == QString("context") ) {
00102             context.truncate( 0 );
00103             source.truncate( 0 );
00104             comment.truncate( 0 );
00105             translation.truncate( 0 );
00106             contextIsUtf8 = encodingIsUtf8( atts );
00107         } else if ( qName == QString("message") ) {
00108             inMessage = TRUE;
00109             type = MetaTranslatorMessage::Finished;
00110             source.truncate( 0 );
00111             comment.truncate( 0 );
00112             translation.truncate( 0 );
00113             messageIsUtf8 = encodingIsUtf8( atts );
00114         } else if ( qName == QString("translation") ) {
00115             for ( int i = 0; i < atts.length(); i++ ) {
00116                 if ( atts.qName(i) == QString("type") ) {
00117                     if ( atts.value(i) == QString("unfinished") )
00118                         type = MetaTranslatorMessage::Unfinished;
00119                     else if ( atts.value(i) == QString("obsolete") )
00120                         type = MetaTranslatorMessage::Obsolete;
00121                     else
00122                         type = MetaTranslatorMessage::Finished;
00123                 }
00124             }
00125         }
00126         accum.truncate( 0 );
00127     }
00128     return TRUE;
00129 }
00130 
00131 bool TsHandler::endElement( const QString& /* namespaceURI */,
00132                             const QString& /* localName */,
00133                             const QString& qName )
00134 {
00135     if ( qName == QString("codec") || qName == QString("defaultcodec") ) {
00136         // "codec" is a pre-3.0 syntax
00137         tor->setCodec( accum );
00138     } else if ( qName == QString("name") ) {
00139         context = accum;
00140     } else if ( qName == QString("source") ) {
00141         source = accum;
00142     } else if ( qName == QString("comment") ) {
00143         if ( inMessage ) {
00144             comment = accum;
00145         } else {
00146             if ( contextIsUtf8 )
00147                 tor->insert( MetaTranslatorMessage(context.utf8(),
00148                              ContextComment,
00149                              accum.utf8(), QString::null, TRUE,
00150                              MetaTranslatorMessage::Unfinished) );
00151             else
00152                 tor->insert( MetaTranslatorMessage(context.ascii(),
00153                              ContextComment,
00154                              accum.ascii(), QString::null, FALSE,
00155                              MetaTranslatorMessage::Unfinished) );
00156         }
00157     } else if ( qName == QString("translation") ) {
00158         translation = accum;
00159     } else if ( qName == QString("message") ) {
00160         if ( messageIsUtf8 )
00161             tor->insert( MetaTranslatorMessage(context.utf8(), source.utf8(),
00162                                                comment.utf8(), translation,
00163                                                TRUE, type) );
00164         else
00165             tor->insert( MetaTranslatorMessage(context.ascii(), source.ascii(),
00166                                                comment.ascii(), translation,
00167                                                FALSE, type) );
00168         inMessage = FALSE;
00169     }
00170     return TRUE;
00171 }
00172 
00173 bool TsHandler::characters( const QString& ch )
00174 {
00175     QString t = ch;
00176     t.replace( "\r", "" );
00177     accum += t;
00178     return TRUE;
00179 }
00180 
00181 bool TsHandler::fatalError( const QXmlParseException& exception )
00182 {
00183     if ( ferrorCount++ == 0 ) {
00184         QString msg;
00185         msg.sprintf( "Parse error at line %d, column %d (%s).",
00186                      exception.lineNumber(), exception.columnNumber(),
00187                      exception.message().latin1() );
00188         if ( qApp == 0 )
00189             fprintf( stderr, "XML error: %s\n", msg.latin1() );
00190         else
00191             QMessageBox::information( qApp->mainWidget(),
00192                                       QObject::tr("Qt Linguist"), msg );
00193     }
00194     return FALSE;
00195 }
00196 
00197 static QString numericEntity( int ch )
00198 {
00199     return QString( ch <= 0x20 ? "<byte value=\"x%1\"/>" : "&#x%1;" )
00200            .arg( ch, 0, 16 );
00201 }
00202 
00203 static QString protect( const QCString& str )
00204 {
00205     QString result;
00206     int len = (int) str.length();
00207     for ( int k = 0; k < len; k++ ) {
00208         switch( str[k] ) {
00209         case '\"':
00210             result += QString( "&quot;" );
00211             break;
00212         case '&':
00213             result += QString( "&amp;" );
00214             break;
00215         case '>':
00216             result += QString( "&gt;" );
00217             break;
00218         case '<':
00219             result += QString( "&lt;" );
00220             break;
00221         case '\'':
00222             result += QString( "&apos;" );
00223             break;
00224         default:
00225             if ( (uchar) str[k] < 0x20 && str[k] != '\n' )
00226                 result += numericEntity( (uchar) str[k] );
00227             else
00228                 result += str[k];
00229         }
00230     }
00231     return result;
00232 }
00233 
00234 static QString evilBytes( const QCString& str, bool utf8 )
00235 {
00236     if ( utf8 ) {
00237         return protect( str );
00238     } else {
00239         QString result;
00240         QCString t = protect( str ).latin1();
00241         int len = (int) t.length();
00242         for ( int k = 0; k < len; k++ ) {
00243             if ( (uchar) t[k] >= 0x7f )
00244                 result += numericEntity( (uchar) t[k] );
00245             else
00246                 result += QChar( t[k] );
00247         }
00248         return result;
00249     }
00250 }
00251 
00252 MetaTranslatorMessage::MetaTranslatorMessage()
00253     : utfeight( FALSE ), ty( Unfinished )
00254 {
00255 }
00256 
00257 MetaTranslatorMessage::MetaTranslatorMessage( const char *context,
00258                                               const char *sourceText,
00259                                               const char *comment,
00260                                               const QString& translation,
00261                                               bool utf8, Type type )
00262     : QTranslatorMessage( context, sourceText, comment, translation ),
00263       utfeight( FALSE ), ty( type )
00264 {
00265     /*
00266       Don't use UTF-8 if it makes no difference. UTF-8 should be
00267       reserved for the real problematic case: non-ASCII (possibly
00268       non-Latin-1) characters in .ui files.
00269     */
00270     if ( utf8 ) {
00271         if ( sourceText != 0 ) {
00272             int i = 0;
00273             while ( sourceText[i] != '\0' ) {
00274                 if ( (uchar) sourceText[i] >= 0x80 ) {
00275                     utfeight = TRUE;
00276                     break;
00277                 }
00278                 i++;
00279             }
00280         }
00281         if ( !utfeight && comment != 0 ) {
00282             int i = 0;
00283             while ( comment[i] != '\0' ) {
00284                 if ( (uchar) comment[i] >= 0x80 ) {
00285                     utfeight = TRUE;
00286                     break;
00287                 }
00288                 i++;
00289             }
00290         }
00291     }
00292 }
00293 
00294 MetaTranslatorMessage::MetaTranslatorMessage( const MetaTranslatorMessage& m )
00295     : QTranslatorMessage( m ), utfeight( m.utfeight ), ty( m.ty )
00296 {
00297 }
00298 
00299 MetaTranslatorMessage& MetaTranslatorMessage::operator=(
00300         const MetaTranslatorMessage& m )
00301 {
00302     QTranslatorMessage::operator=( m );
00303     utfeight = m.utfeight;
00304     ty = m.ty;
00305     return *this;
00306 }
00307 
00308 bool MetaTranslatorMessage::operator==( const MetaTranslatorMessage& m ) const
00309 {
00310     return qstrcmp( context(), m.context() ) == 0 &&
00311            qstrcmp( sourceText(), m.sourceText() ) == 0 &&
00312            qstrcmp( comment(), m.comment() ) == 0;
00313 }
00314 
00315 bool MetaTranslatorMessage::operator<( const MetaTranslatorMessage& m ) const
00316 {
00317     int delta = qstrcmp( context(), m.context() );
00318     if ( delta == 0 )
00319         delta = qstrcmp( sourceText(), m.sourceText() );
00320     if ( delta == 0 )
00321         delta = qstrcmp( comment(), m.comment() );
00322     return delta < 0;
00323 }
00324 
00325 MetaTranslator::MetaTranslator()
00326 {
00327     clear();
00328 }
00329 
00330 MetaTranslator::MetaTranslator( const MetaTranslator& tor )
00331     : mm( tor.mm ), codecName( tor.codecName ), codec( tor.codec )
00332 {
00333 }
00334 
00335 MetaTranslator& MetaTranslator::operator=( const MetaTranslator& tor )
00336 {
00337     mm = tor.mm;
00338     codecName = tor.codecName;
00339     codec = tor.codec;
00340     return *this;
00341 }
00342 
00343 void MetaTranslator::clear()
00344 {
00345     mm.clear();
00346     codecName = "ISO-8859-1";
00347     codec = 0;
00348 }
00349 
00350 bool MetaTranslator::load( const QString& filename )
00351 {
00352     QFile f( filename );
00353     if ( !f.open(IO_ReadOnly) )
00354         return FALSE;
00355 
00356     QTextStream t( &f );
00357     QXmlInputSource in( t );
00358     QXmlSimpleReader reader;
00359     reader.setFeature( "http://xml.org/sax/features/namespaces", FALSE );
00360     reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", TRUE );
00361     reader.setFeature( "http://trolltech.com/xml/features/report-whitespace"
00362                        "-only-CharData", FALSE );
00363     QXmlDefaultHandler *hand = new TsHandler( this );
00364     reader.setContentHandler( hand );
00365     reader.setErrorHandler( hand );
00366 
00367     bool ok = reader.parse( in );
00368     reader.setContentHandler( 0 );
00369     reader.setErrorHandler( 0 );
00370     delete hand;
00371     f.close();
00372     return ok;
00373 }
00374 
00375 bool MetaTranslator::save( const QString& filename ) const
00376 {
00377     QFile f( filename );
00378     if ( !f.open(IO_WriteOnly) )
00379         return FALSE;
00380 
00381     QTextStream t( &f );
00382     t.setCodec( QTextCodec::codecForName("ISO-8859-1") );
00383 
00384     t << "<!DOCTYPE TS><TS>\n";
00385     if ( codecName != "ISO-8859-1" )
00386         t << "<defaultcodec>" << codecName << "</defaultcodec>\n";
00387     TMM::ConstIterator m = mm.begin();
00388     while ( m != mm.end() ) {
00389         TMMInv inv;
00390         TMMInv::Iterator i;
00391         bool contextIsUtf8 = m.key().utf8();
00392         QCString context = m.key().context();
00393         QCString comment = "";
00394 
00395         do {
00396             if ( QCString(m.key().sourceText()) == ContextComment ) {
00397                 if ( m.key().type() != MetaTranslatorMessage::Obsolete ) {
00398                     contextIsUtf8 = m.key().utf8();
00399                     comment = QCString( m.key().comment() );
00400                 }
00401             } else {
00402                 inv.insert( *m, m.key() );
00403             }
00404         } while ( ++m != mm.end() && QCString(m.key().context()) == context );
00405 
00406         t << "<context";
00407         if ( contextIsUtf8 )
00408             t << " encoding=\"UTF-8\"";
00409         t << ">\n";
00410         t << "    <name>" << evilBytes( context, contextIsUtf8 )
00411           << "</name>\n";
00412         if ( !comment.isEmpty() )
00413             t << "    <comment>" << evilBytes( comment, contextIsUtf8 )
00414               << "</comment>\n";
00415 
00416         for ( i = inv.begin(); i != inv.end(); ++i ) {
00417             // no need for such noise
00418             if ( (*i).type() == MetaTranslatorMessage::Obsolete &&
00419                  (*i).translation().isEmpty() )
00420                 continue;
00421 
00422             t << "    <message";
00423             if ( (*i).utf8() )
00424                 t << " encoding=\"UTF-8\"";
00425             t << ">\n"
00426               << "        <source>" << evilBytes( (*i).sourceText(),
00427                                                   (*i).utf8() )
00428               << "</source>\n";
00429             if ( !QCString((*i).comment()).isEmpty() )
00430                 t << "        <comment>" << evilBytes( (*i).comment(),
00431                                                        (*i).utf8() )
00432                   << "</comment>\n";
00433             t << "        <translation";
00434             if ( (*i).type() == MetaTranslatorMessage::Unfinished )
00435                 t << " type=\"unfinished\"";
00436             else if ( (*i).type() == MetaTranslatorMessage::Obsolete )
00437                 t << " type=\"obsolete\"";
00438             t << ">" << protect( (*i).translation().utf8() )
00439               << "</translation>\n";
00440             t << "    </message>\n";
00441         }
00442         t << "</context>\n";
00443     }
00444     t << "</TS>\n";
00445     f.close();
00446     return TRUE;
00447 }
00448 
00449 bool MetaTranslator::release( const QString& filename, bool verbose,
00450                               QTranslator::SaveMode mode ) const
00451 {
00452     QTranslator tor( 0 );
00453     int finished = 0;
00454     int unfinished = 0;
00455     int untranslated = 0;
00456     TMM::ConstIterator m;
00457 
00458     for ( m = mm.begin(); m != mm.end(); ++m ) {
00459         if ( m.key().type() != MetaTranslatorMessage::Obsolete ) {
00460             if ( m.key().translation().isEmpty() ) {
00461                 untranslated++;
00462             } else {
00463                 if ( m.key().type() == MetaTranslatorMessage::Unfinished )
00464                     unfinished++;
00465                 else
00466                     finished++;
00467 
00468                 QCString context = m.key().context();
00469                 QCString sourceText = m.key().sourceText();
00470                 QCString comment = m.key().comment();
00471                 QString translation = m.key().translation();
00472 
00473                 /*
00474                   Drop the comment in (context, sourceText, comment),
00475                   unless (context, sourceText, "") already exists, or
00476                   unless we already dropped the comment of (context,
00477                   sourceText, comment0).
00478                 */
00479                 if ( comment.isEmpty()
00480                      || contains(context, sourceText, "")
00481                      || !tor.findMessage(context, sourceText, "").translation()
00482                             .isNull() ) {
00483                     tor.insert( m.key() );
00484                 } else {
00485                     tor.insert( QTranslatorMessage(context, sourceText, "",
00486                                                    translation) );
00487                 }
00488             }
00489         }
00490     }
00491 
00492     bool saved = tor.save( filename, mode );
00493     if ( saved && verbose )
00494         fprintf( stderr,
00495                  " %d finished, %d unfinished and %d untranslated messages\n",
00496                  finished, unfinished, untranslated );
00497                 
00498     return saved;
00499 }
00500 
00501 bool MetaTranslator::contains( const char *context, const char *sourceText,
00502                                const char *comment ) const
00503 {
00504     return mm.find( MetaTranslatorMessage(context, sourceText, comment) ) !=
00505            mm.end();
00506 }
00507 
00508 void MetaTranslator::insert( const MetaTranslatorMessage& m )
00509 {
00510     int pos = mm.count();
00511     TMM::Iterator n = mm.find( m );
00512     if ( n != mm.end() )
00513         pos = *n;
00514     mm.replace( m, pos );
00515 }
00516 
00517 void MetaTranslator::stripObsoleteMessages()
00518 {
00519     TMM newmm;
00520 
00521     TMM::Iterator m = mm.begin();
00522     while ( m != mm.end() ) {
00523         if ( m.key().type() != MetaTranslatorMessage::Obsolete )
00524             newmm.insert( m.key(), *m );
00525         ++m;
00526     }
00527     mm = newmm;
00528 }
00529 
00530 void MetaTranslator::stripEmptyContexts()
00531 {
00532     TMM newmm;
00533 
00534     TMM::Iterator m = mm.begin();
00535     while ( m != mm.end() ) {
00536         if ( QCString(m.key().sourceText()) == ContextComment ) {
00537             TMM::Iterator n = m;
00538             ++n;
00539             // the context comment is followed by other messages
00540             if ( n != newmm.end() &&
00541                  qstrcmp(m.key().context(), n.key().context()) == 0 )
00542                 newmm.insert( m.key(), *m );
00543         } else {
00544             newmm.insert( m.key(), *m );
00545         }
00546         ++m;
00547     }
00548     mm = newmm;
00549 }
00550 
00551 void MetaTranslator::setCodec( const char *name )
00552 {
00553     const int latin1 = 4;
00554 
00555     codecName = name;
00556     codec = QTextCodec::codecForName( name );
00557     if ( codec == 0 || codec->mibEnum() == latin1 )
00558         codec = 0;
00559 }
00560 
00561 QString MetaTranslator::toUnicode( const char *str, bool utf8 ) const
00562 {
00563     if ( utf8 )
00564         return QString::fromUtf8( str );
00565     else if ( codec == 0 )
00566         return QString( str );
00567     else
00568         return codec->toUnicode( str );
00569 }
00570 
00571 QValueList<MetaTranslatorMessage> MetaTranslator::messages() const
00572 {
00573     int n = mm.count();
00574     TMM::ConstIterator *t = new TMM::ConstIterator[n + 1];
00575     TMM::ConstIterator m;
00576     for ( m = mm.begin(); m != mm.end(); ++m )
00577         t[*m] = m;
00578 
00579     QValueList<MetaTranslatorMessage> val;
00580     for ( int i = 0; i < n; i++ )
00581         val.append( t[i].key() );
00582 
00583     delete[] t;
00584     return val;
00585 }
00586 
00587 QValueList<MetaTranslatorMessage> MetaTranslator::translatedMessages() const
00588 {
00589     QValueList<MetaTranslatorMessage> val;
00590     TMM::ConstIterator m;
00591     for ( m = mm.begin(); m != mm.end(); ++m ) {
00592         if ( m.key().type() == MetaTranslatorMessage::Finished )
00593             val.append( m.key() );
00594     }
00595     return val;
00596 }

Generated on Sat Nov 5 16:15:58 2005 for OPIE by  doxygen 1.4.2