00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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
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& ,
00082 const QString& ,
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& ,
00132 const QString& ,
00133 const QString& qName )
00134 {
00135 if ( qName == QString("codec") || qName == QString("defaultcodec") ) {
00136
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( """ );
00211 break;
00212 case '&':
00213 result += QString( "&" );
00214 break;
00215 case '>':
00216 result += QString( ">" );
00217 break;
00218 case '<':
00219 result += QString( "<" );
00220 break;
00221 case '\'':
00222 result += QString( "'" );
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
00267
00268
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
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
00475
00476
00477
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
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 }