SIP4 python bindings for TQt
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

558 lines
14KB

  1. /**********************************************************************
  2. ** Copyright (C) 2000 Trolltech AS. All rights reserved.
  3. **
  4. ** metatranslator.cpp
  5. **
  6. ** This file is part of TQt Linguist.
  7. **
  8. ** See the file LICENSE included in the distribution for the usage
  9. ** and distribution terms.
  10. **
  11. ** The file is provided AS IS with NO WARRANTY OF ANY KIND,
  12. ** INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR
  13. ** A PARTICULAR PURPOSE.
  14. **
  15. **********************************************************************/
  16. #include <qapplication.h>
  17. #include <qcstring.h>
  18. #include <qfile.h>
  19. #include <qmessagebox.h>
  20. #include <qregexp.h>
  21. #include <qtextcodec.h>
  22. #include <qtextstream.h>
  23. #include <qxml.h>
  24. #include "metatranslator.h"
  25. static bool encodingIsUtf8( const TQXmlAttributes& atts )
  26. {
  27. for ( int i = 0; i < atts.length(); i++ ) {
  28. // utf8="true" is a pre-3.0 syntax
  29. if ( atts.qName(i) == TQString("utf8") ) {
  30. return ( atts.value(i) == TQString("true") );
  31. } else if ( atts.qName(i) == TQString("encoding") ) {
  32. return ( atts.value(i) == TQString("UTF-8") );
  33. }
  34. }
  35. return FALSE;
  36. }
  37. class TsHandler : public TQXmlDefaultHandler
  38. {
  39. public:
  40. TsHandler( MetaTranslator *translator )
  41. : tor( translator ), type( MetaTranslatorMessage::Finished ),
  42. inMessage( FALSE ), ferrorCount( 0 ), contextIsUtf8( FALSE ),
  43. messageIsUtf8( FALSE ) { }
  44. virtual bool startElement( const TQString& namespaceURI,
  45. const TQString& localName, const TQString& qName,
  46. const TQXmlAttributes& atts );
  47. virtual bool endElement( const TQString& namespaceURI,
  48. const TQString& localName, const TQString& qName );
  49. virtual bool characters( const TQString& ch );
  50. virtual bool fatalError( const TQXmlParseException& exception );
  51. private:
  52. MetaTranslator *tor;
  53. MetaTranslatorMessage::Type type;
  54. bool inMessage;
  55. TQString context;
  56. TQString source;
  57. TQString comment;
  58. TQString translation;
  59. TQString accum;
  60. int ferrorCount;
  61. bool contextIsUtf8;
  62. bool messageIsUtf8;
  63. };
  64. bool TsHandler::startElement( const TQString& /* namespaceURI */,
  65. const TQString& /* localName */,
  66. const TQString& qName,
  67. const TQXmlAttributes& atts )
  68. {
  69. if ( qName == TQString("byte") ) {
  70. for ( int i = 0; i < atts.length(); i++ ) {
  71. if ( atts.qName(i) == TQString("value") ) {
  72. TQString value = atts.value( i );
  73. int base = 10;
  74. if ( value.startsWith("x") ) {
  75. base = 16;
  76. value = value.mid( 1 );
  77. }
  78. int n = value.toUInt( 0, base );
  79. if ( n != 0 )
  80. accum += TQChar( n );
  81. }
  82. }
  83. } else {
  84. if ( qName == TQString("context") ) {
  85. context.truncate( 0 );
  86. source.truncate( 0 );
  87. comment.truncate( 0 );
  88. translation.truncate( 0 );
  89. contextIsUtf8 = encodingIsUtf8( atts );
  90. } else if ( qName == TQString("message") ) {
  91. inMessage = TRUE;
  92. type = MetaTranslatorMessage::Finished;
  93. source.truncate( 0 );
  94. comment.truncate( 0 );
  95. translation.truncate( 0 );
  96. messageIsUtf8 = encodingIsUtf8( atts );
  97. } else if ( qName == TQString("translation") ) {
  98. for ( int i = 0; i < atts.length(); i++ ) {
  99. if ( atts.qName(i) == TQString("type") ) {
  100. if ( atts.value(i) == TQString("unfinished") )
  101. type = MetaTranslatorMessage::Unfinished;
  102. else if ( atts.value(i) == TQString("obsolete") )
  103. type = MetaTranslatorMessage::Obsolete;
  104. else
  105. type = MetaTranslatorMessage::Finished;
  106. }
  107. }
  108. }
  109. accum.truncate( 0 );
  110. }
  111. return TRUE;
  112. }
  113. bool TsHandler::endElement( const TQString& /* namespaceURI */,
  114. const TQString& /* localName */,
  115. const TQString& qName )
  116. {
  117. if ( qName == TQString("codec") || qName == TQString("defaultcodec") ) {
  118. // "codec" is a pre-3.0 syntax
  119. tor->setCodec( accum );
  120. } else if ( qName == TQString("name") ) {
  121. context = accum;
  122. } else if ( qName == TQString("source") ) {
  123. source = accum;
  124. } else if ( qName == TQString("comment") ) {
  125. if ( inMessage ) {
  126. comment = accum;
  127. } else {
  128. if ( contextIsUtf8 )
  129. tor->insert( MetaTranslatorMessage(context.utf8(), "",
  130. accum.utf8(), TQString::null, TRUE,
  131. MetaTranslatorMessage::Unfinished) );
  132. else
  133. tor->insert( MetaTranslatorMessage(context.ascii(), "",
  134. accum.ascii(), TQString::null, FALSE,
  135. MetaTranslatorMessage::Unfinished) );
  136. }
  137. } else if ( qName == TQString("translation") ) {
  138. translation = accum;
  139. } else if ( qName == TQString("message") ) {
  140. if ( messageIsUtf8 )
  141. tor->insert( MetaTranslatorMessage(context.utf8(), source.utf8(),
  142. comment.utf8(), translation,
  143. TRUE, type) );
  144. else
  145. tor->insert( MetaTranslatorMessage(context.ascii(), source.ascii(),
  146. comment.ascii(), translation,
  147. FALSE, type) );
  148. inMessage = FALSE;
  149. }
  150. return TRUE;
  151. }
  152. bool TsHandler::characters( const TQString& ch )
  153. {
  154. TQString t = ch;
  155. t.replace( TQRegExp(TQChar('\r')), "" );
  156. accum += t;
  157. return TRUE;
  158. }
  159. bool TsHandler::fatalError( const TQXmlParseException& exception )
  160. {
  161. if ( ferrorCount++ == 0 ) {
  162. TQString msg;
  163. msg.sprintf( "Parse error at line %d, column %d (%s).",
  164. exception.lineNumber(), exception.columnNumber(),
  165. exception.message().latin1() );
  166. if ( qApp == 0 )
  167. qWarning( "XML error: %s", msg.latin1() );
  168. else
  169. TQMessageBox::information( qApp->mainWidget(),
  170. TQObject::tr("TQt Linguist"), msg );
  171. }
  172. return FALSE;
  173. }
  174. static TQString numericEntity( int ch )
  175. {
  176. return TQString( ch <= 0x20 ? "<byte value=\"x%1\"/>" : "&#x%1;" )
  177. .arg( ch, 0, 16 );
  178. }
  179. static TQString protect( const TQCString& str )
  180. {
  181. TQString result;
  182. int len = (int) str.length();
  183. for ( int k = 0; k < len; k++ ) {
  184. switch( str[k] ) {
  185. case '\"':
  186. result += TQString( "&quot;" );
  187. break;
  188. case '&':
  189. result += TQString( "&amp;" );
  190. break;
  191. case '>':
  192. result += TQString( "&gt;" );
  193. break;
  194. case '<':
  195. result += TQString( "&lt;" );
  196. break;
  197. case '\'':
  198. result += TQString( "&apos;" );
  199. break;
  200. default:
  201. if ( (uchar) str[k] < 0x20 && str[k] != '\n' )
  202. result += numericEntity( (uchar) str[k] );
  203. else
  204. result += str[k];
  205. }
  206. }
  207. return result;
  208. }
  209. static TQString evilBytes( const TQCString& str, bool utf8 )
  210. {
  211. if ( utf8 ) {
  212. return protect( str );
  213. } else {
  214. TQString result;
  215. TQCString t = protect( str ).latin1();
  216. int len = (int) t.length();
  217. for ( int k = 0; k < len; k++ ) {
  218. if ( (uchar) t[k] >= 0x7f )
  219. result += numericEntity( (uchar) t[k] );
  220. else
  221. result += TQChar( t[k] );
  222. }
  223. return result;
  224. }
  225. }
  226. MetaTranslatorMessage::MetaTranslatorMessage()
  227. : utfeight( FALSE ), ty( Unfinished )
  228. {
  229. }
  230. MetaTranslatorMessage::MetaTranslatorMessage( const char *context,
  231. const char *sourceText,
  232. const char *comment,
  233. const TQString& translation,
  234. bool utf8, Type type )
  235. : TQTranslatorMessage( context, sourceText, comment, translation ),
  236. utfeight( FALSE ), ty( type )
  237. {
  238. /*
  239. Don't use UTF-8 if it makes no difference. UTF-8 should be
  240. reserved for the real problematic case: non-ASCII (possibly
  241. non-Latin-1) characters in .ui files.
  242. */
  243. if ( utf8 ) {
  244. if ( sourceText != 0 ) {
  245. int i = 0;
  246. while ( sourceText[i] != '\0' ) {
  247. if ( (uchar) sourceText[i] >= 0x80 ) {
  248. utfeight = TRUE;
  249. break;
  250. }
  251. i++;
  252. }
  253. }
  254. if ( !utfeight && comment != 0 ) {
  255. int i = 0;
  256. while ( comment[i] != '\0' ) {
  257. if ( (uchar) comment[i] >= 0x80 ) {
  258. utfeight = TRUE;
  259. break;
  260. }
  261. i++;
  262. }
  263. }
  264. }
  265. }
  266. MetaTranslatorMessage::MetaTranslatorMessage( const MetaTranslatorMessage& m )
  267. : TQTranslatorMessage( m ), utfeight( m.utfeight ), ty( m.ty )
  268. {
  269. }
  270. MetaTranslatorMessage& MetaTranslatorMessage::operator=(
  271. const MetaTranslatorMessage& m )
  272. {
  273. TQTranslatorMessage::operator=( m );
  274. utfeight = m.utfeight;
  275. ty = m.ty;
  276. return *this;
  277. }
  278. bool MetaTranslatorMessage::operator==( const MetaTranslatorMessage& m ) const
  279. {
  280. return qstrcmp( context(), m.context() ) == 0 &&
  281. qstrcmp( sourceText(), m.sourceText() ) == 0 &&
  282. qstrcmp( comment(), m.comment() ) == 0;
  283. }
  284. bool MetaTranslatorMessage::operator<( const MetaTranslatorMessage& m ) const
  285. {
  286. int delta = qstrcmp( context(), m.context() );
  287. if ( delta == 0 )
  288. delta = qstrcmp( sourceText(), m.sourceText() );
  289. if ( delta == 0 )
  290. delta = qstrcmp( comment(), m.comment() );
  291. return delta < 0;
  292. }
  293. MetaTranslator::MetaTranslator()
  294. : codecName( "ISO-8859-1" ), codec( 0 )
  295. {
  296. }
  297. MetaTranslator::MetaTranslator( const MetaTranslator& tor )
  298. : mm( tor.mm ), codecName( tor.codecName ), codec( tor.codec )
  299. {
  300. }
  301. MetaTranslator& MetaTranslator::operator=( const MetaTranslator& tor )
  302. {
  303. mm = tor.mm;
  304. codecName = tor.codecName;
  305. codec = tor.codec;
  306. return *this;
  307. }
  308. bool MetaTranslator::load( const TQString& filename )
  309. {
  310. mm.clear();
  311. TQFile f( filename );
  312. if ( !f.open(IO_ReadOnly) )
  313. return FALSE;
  314. TQTextStream t( &f );
  315. TQXmlInputSource in( t );
  316. TQXmlSimpleReader reader;
  317. // don't click on these!
  318. reader.setFeature( "http://xml.org/sax/features/namespaces", FALSE );
  319. reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", TRUE );
  320. reader.setFeature( "http://trolltech.com/xml/features/report-whitespace"
  321. "-only-CharData", FALSE );
  322. TQXmlDefaultHandler *hand = new TsHandler( this );
  323. reader.setContentHandler( hand );
  324. reader.setErrorHandler( hand );
  325. bool ok = reader.parse( in );
  326. reader.setContentHandler( 0 );
  327. reader.setErrorHandler( 0 );
  328. delete hand;
  329. f.close();
  330. if ( !ok )
  331. mm.clear();
  332. return ok;
  333. }
  334. bool MetaTranslator::save( const TQString& filename ) const
  335. {
  336. TQFile f( filename );
  337. if ( !f.open(IO_WriteOnly) )
  338. return FALSE;
  339. TQTextStream t( &f );
  340. t.setCodec( TQTextCodec::codecForName("ISO-8859-1") );
  341. t << "<!DOCTYPE TS><TS>\n";
  342. if ( codecName != "ISO-8859-1" )
  343. t << "<defaultcodec>" << codecName << "</defaultcodec>\n";
  344. TMM::ConstIterator m = mm.begin();
  345. while ( m != mm.end() ) {
  346. TMMInv inv;
  347. TMMInv::Iterator i;
  348. bool contextIsUtf8 = m.key().utf8();
  349. TQCString context = m.key().context();
  350. TQCString comment = "";
  351. do {
  352. if ( TQCString(m.key().sourceText()).isEmpty() ) {
  353. if ( m.key().type() != MetaTranslatorMessage::Obsolete ) {
  354. contextIsUtf8 = m.key().utf8();
  355. comment = TQCString( m.key().comment() );
  356. }
  357. } else {
  358. inv.insert( *m, m.key() );
  359. }
  360. } while ( ++m != mm.end() && TQCString(m.key().context()) == context );
  361. t << "<context";
  362. if ( contextIsUtf8 )
  363. t << " encoding=\"UTF-8\"";
  364. t << ">\n";
  365. t << " <name>" << evilBytes( context, contextIsUtf8 )
  366. << "</name>\n";
  367. if ( !comment.isEmpty() )
  368. t << " <comment>" << evilBytes( comment, contextIsUtf8 )
  369. << "</comment>\n";
  370. for ( i = inv.begin(); i != inv.end(); ++i ) {
  371. t << " <message";
  372. if ( (*i).utf8() )
  373. t << " encoding=\"UTF-8\"";
  374. t << ">\n"
  375. << " <source>" << evilBytes( (*i).sourceText(),
  376. (*i).utf8() )
  377. << "</source>\n";
  378. if ( !TQCString((*i).comment()).isEmpty() )
  379. t << " <comment>" << evilBytes( (*i).comment(),
  380. (*i).utf8() )
  381. << "</comment>\n";
  382. t << " <translation";
  383. if ( (*i).type() == MetaTranslatorMessage::Unfinished )
  384. t << " type=\"unfinished\"";
  385. else if ( (*i).type() == MetaTranslatorMessage::Obsolete )
  386. t << " type=\"obsolete\"";
  387. t << ">" << protect( (*i).translation().utf8() )
  388. << "</translation>\n";
  389. t << " </message>\n";
  390. }
  391. t << "</context>\n";
  392. }
  393. t << "</TS>\n";
  394. f.close();
  395. return TRUE;
  396. }
  397. bool MetaTranslator::release( const TQString& filename, bool verbose ) const
  398. {
  399. TQTranslator tor( 0 );
  400. int finished = 0;
  401. int unfinished = 0;
  402. int untranslated = 0;
  403. TMM::ConstIterator m;
  404. for ( m = mm.begin(); m != mm.end(); ++m ) {
  405. if ( m.key().type() != MetaTranslatorMessage::Obsolete ) {
  406. if ( m.key().translation().isEmpty() ) {
  407. untranslated++;
  408. } else {
  409. if ( m.key().type() == MetaTranslatorMessage::Unfinished )
  410. unfinished++;
  411. else
  412. finished++;
  413. tor.insert( m.key() );
  414. }
  415. }
  416. }
  417. bool saved = tor.save( filename, TQTranslator::Stripped );
  418. if ( saved && verbose )
  419. qWarning( " %d finished, %d unfinished and %d untranslated messages",
  420. finished, unfinished, untranslated );
  421. return saved;
  422. }
  423. bool MetaTranslator::contains( const char *context, const char *sourceText,
  424. const char *comment ) const
  425. {
  426. return mm.find( MetaTranslatorMessage(context, sourceText, comment) ) !=
  427. mm.end();
  428. }
  429. void MetaTranslator::insert( const MetaTranslatorMessage& m )
  430. {
  431. int pos = mm.count();
  432. TMM::Iterator n = mm.find( m );
  433. if ( n != mm.end() )
  434. pos = *n;
  435. mm.replace( m, pos );
  436. }
  437. void MetaTranslator::stripObsoleteMessages()
  438. {
  439. TMM newmm;
  440. TMM::Iterator m = mm.begin();
  441. while ( m != mm.end() ) {
  442. if ( m.key().type() != MetaTranslatorMessage::Obsolete )
  443. newmm.insert( m.key(), *m );
  444. ++m;
  445. }
  446. mm = newmm;
  447. }
  448. void MetaTranslator::stripEmptyContexts()
  449. {
  450. TMM newmm;
  451. TMM::Iterator m = mm.begin();
  452. while ( m != mm.end() ) {
  453. if ( TQCString(m.key().sourceText()).isEmpty() ) {
  454. TMM::Iterator n = m;
  455. ++n;
  456. // the context comment is followed by other messages
  457. if ( n != newmm.end() &&
  458. qstrcmp(m.key().context(), n.key().context()) == 0 )
  459. newmm.insert( m.key(), *m );
  460. } else {
  461. newmm.insert( m.key(), *m );
  462. }
  463. ++m;
  464. }
  465. mm = newmm;
  466. }
  467. void MetaTranslator::setCodec( const char *name )
  468. {
  469. const int latin1 = 4;
  470. codecName = name;
  471. codec = TQTextCodec::codecForName( name );
  472. if ( codec == 0 || codec->mibEnum() == latin1 )
  473. codec = 0;
  474. }
  475. TQString MetaTranslator::toUnicode( const char *str, bool utf8 ) const
  476. {
  477. if ( utf8 )
  478. return TQString::fromUtf8( str );
  479. else if ( codec == 0 )
  480. return TQString( str );
  481. else
  482. return codec->toUnicode( str );
  483. }
  484. TQValueList<MetaTranslatorMessage> MetaTranslator::messages() const
  485. {
  486. int n = mm.count();
  487. TMM::ConstIterator *t = new TMM::ConstIterator[n + 1];
  488. TMM::ConstIterator m;
  489. for ( m = mm.begin(); m != mm.end(); ++m )
  490. t[*m] = m;
  491. TQValueList<MetaTranslatorMessage> val;
  492. for ( int i = 0; i < n; i++ )
  493. val.append( t[i].key() );
  494. delete[] t;
  495. return val;
  496. }
  497. TQValueList<MetaTranslatorMessage> MetaTranslator::translatedMessages() const
  498. {
  499. TQValueList<MetaTranslatorMessage> val;
  500. TMM::ConstIterator m;
  501. for ( m = mm.begin(); m != mm.end(); ++m ) {
  502. if ( m.key().type() == MetaTranslatorMessage::Finished )
  503. val.append( m.key() );
  504. }
  505. return val;
  506. }