/* translatorplugin.cpp Kopete Translator plugin Copyright (c) 2001-2002 by Duncan Mac-Vicar Prett Copyright (c) 2002-2004 by Olivier Goffart Kopete (c) 2002-2004 by the Kopete developers ************************************************************************* * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ************************************************************************* Patched by Francesco Rossi in order to support new google translation page layout (13-sept-2007) */ #include #include #include #include #include #include #include #include #include #include #include #include "kopetemetacontact.h" #include "kopetecontactlist.h" #include "kopetechatsessionmanager.h" #include "kopetecontact.h" #include "translatorplugin.h" #include "translatordialog.h" #include "translatorguiclient.h" #include "translatorlanguages.h" typedef KGenericFactory TranslatorPluginFactory; #if KDE_IS_VERSION(3,2,90) static const TDEAboutData aboutdata("kopete_translator", I18N_NOOP("Translator") , "1.0" ); K_EXPORT_COMPONENT_FACTORY( kopete_translator, TranslatorPluginFactory( &aboutdata ) ) #else K_EXPORT_COMPONENT_FACTORY( kopete_translator, TranslatorPluginFactory( "kopete_translator" ) ) #endif TranslatorPlugin::TranslatorPlugin( TQObject *parent, const char *name, const TQStringList & /* args */ ) : Kopete::Plugin( TranslatorPluginFactory::instance(), parent, name ) { kdDebug( 14308 ) << k_funcinfo << endl; if ( pluginStatic_ ) kdWarning( 14308 ) << k_funcinfo << "Translator already initialized" << endl; else pluginStatic_ = this; m_languages = new TranslatorLanguages; connect( Kopete::ChatSessionManager::self(), TQT_SIGNAL( aboutToDisplay( Kopete::Message & ) ), this, TQT_SLOT( slotIncomingMessage( Kopete::Message & ) ) ); connect( Kopete::ChatSessionManager::self(), TQT_SIGNAL( aboutToSend( Kopete::Message & ) ), this, TQT_SLOT( slotOutgoingMessage( Kopete::Message & ) ) ); connect( Kopete::ChatSessionManager::self(), TQT_SIGNAL( chatSessionCreated( Kopete::ChatSession * ) ), this, TQT_SLOT( slotNewKMM( Kopete::ChatSession * ) ) ); TQStringList keys; TQMap m = m_languages->languagesMap(); for ( int k = 0; k <= m_languages->numLanguages(); k++ ) keys << m[ m_languages->languageKey( k ) ]; m_actionLanguage = new TDESelectAction( i18n( "Set &Language" ), "locale", 0, actionCollection(), "contactLanguage" ); m_actionLanguage->setItems( keys ); connect( m_actionLanguage, TQT_SIGNAL( activated() ), this, TQT_SLOT(slotSetLanguage() ) ); connect( Kopete::ContactList::self(), TQT_SIGNAL( metaContactSelected( bool ) ), this, TQT_SLOT( slotSelectionChanged( bool ) ) ); setXMLFile( "translatorui.rc" ); //Add GUI action to all already existing kmm (if the plugin is launched when kopete already rining) TQValueList sessions = Kopete::ChatSessionManager::self()->sessions(); for (TQValueListIterator it= sessions.begin(); it!=sessions.end() ; ++it) slotNewKMM( *it ); loadSettings(); connect( this, TQT_SIGNAL( settingsChanged() ), this, TQT_SLOT( loadSettings() ) ); } TranslatorPlugin::~TranslatorPlugin() { kdDebug( 14308 ) << k_funcinfo << endl; pluginStatic_ = 0L; } TranslatorPlugin* TranslatorPlugin::plugin() { return pluginStatic_; } TranslatorPlugin* TranslatorPlugin::pluginStatic_ = 0L; void TranslatorPlugin::loadSettings() { TDEConfig *config = TDEGlobal::config(); int mode = 0; config->setGroup( "Translator Plugin" ); m_myLang = m_languages->languageKey( config->readNumEntry( "myLang" , 0 ) ); m_service = m_languages->serviceKey( config->readNumEntry( "Service", 0 ) ); if ( config->readBoolEntry( "IncomingDontTranslate", true ) ) mode = 0; else if ( config->readBoolEntry( "IncomingShowOriginal", false ) ) mode = 1; else if ( config->readBoolEntry( "IncomingTranslate", false ) ) mode = 2; m_incomingMode = mode; if ( config->readBoolEntry( "OutgoingDontTranslate", true ) ) mode = 0; else if ( config->readBoolEntry( "OutgoingShowOriginal", false ) ) mode = 1; else if ( config->readBoolEntry( "OutgoingTranslate", false ) ) mode = 2; else if ( config->readBoolEntry( "OutgoingAsk", false ) ) mode = 3; m_outgoingMode = mode; } void TranslatorPlugin::slotSelectionChanged( bool b ) { m_actionLanguage->setEnabled( b ); if ( !b ) return; Kopete::MetaContact *m = Kopete::ContactList::self()->selectedMetaContacts().first(); if( !m ) return; TQString languageKey = m->pluginData( this, "languageKey" ); if ( !languageKey.isEmpty() && languageKey != "null" ) m_actionLanguage->setCurrentItem( m_languages->languageIndex( languageKey ) ); else m_actionLanguage->setCurrentItem( m_languages->languageIndex( "null" ) ); } void TranslatorPlugin::slotNewKMM( Kopete::ChatSession *KMM ) { new TranslatorGUIClient( KMM ); } void TranslatorPlugin::slotIncomingMessage( Kopete::Message &msg ) { if ( m_incomingMode == DontTranslate ) return; TQString src_lang; TQString dst_lang; if ( ( msg.direction() == Kopete::Message::Inbound ) && !msg.plainBody().isEmpty() ) { Kopete::MetaContact *from = msg.from()->metaContact(); if ( !from ) { // kdDebug( 14308 ) << k_funcinfo << "No metaContact for source contact" << endl; return; } src_lang = from->pluginData( this, "languageKey" ); if( src_lang.isEmpty() || src_lang == "null" ) { // kdDebug( 14308 ) << k_funcinfo << "Cannot determine src Metacontact language (" << from->displayName() << ")" << endl; return; } dst_lang = m_myLang; sendTranslation( msg, translateMessage( msg.plainBody(), src_lang, dst_lang ) ); } } void TranslatorPlugin::slotOutgoingMessage( Kopete::Message &msg ) { if ( m_outgoingMode == DontTranslate ) return; TQString src_lang; TQString dst_lang; if ( ( msg.direction() == Kopete::Message::Outbound ) && ( !msg.plainBody().isEmpty() ) ) { src_lang = m_myLang; // Sad, we have to consider only the first To: metacontact Kopete::MetaContact *to = msg.to().first()->metaContact(); if ( !to ) { // kdDebug( 14308 ) << k_funcinfo << "No metaContact for first contact" << endl; return; } dst_lang = to->pluginData( this, "languageKey" ); if ( dst_lang.isEmpty() || dst_lang == "null" ) { // kdDebug( 14308 ) << k_funcinfo << "Cannot determine dst Metacontact language (" << to->displayName() << ")" << endl; return; } sendTranslation( msg, translateMessage( msg.plainBody(), src_lang, dst_lang ) ); } } void TranslatorPlugin::translateMessage( const TQString &msg, const TQString &from, const TQString &to, TQObject *obj, const char* slot ) { TQSignal completeSignal; completeSignal.connect( obj, slot ); TQString result = translateMessage( msg, from, to ); if(!result.isNull()) { completeSignal.setValue( result ); completeSignal.activate(); } } TQString TranslatorPlugin::translateMessage( const TQString &msg, const TQString &from, const TQString &to ) { if ( from == to ) { kdDebug( 14308 ) << k_funcinfo << "Src and Dst languages are the same" << endl; return TQString(); } // We search for src_dst if(! m_languages->supported( m_service ).contains( from + "_" + to ) ) { kdDebug( 14308 ) << k_funcinfo << from << "_" << to << " is not supported by service " << m_service << endl; return TQString(); } if ( m_service == "babelfish" ) return babelTranslateMessage( msg ,from, to ); else if ( m_service == "google" ) return googleTranslateMessage( msg ,from, to ); else return TQString(); } TQString TranslatorPlugin::googleTranslateMessage( const TQString &msg, const TQString &from, const TQString &to ) { KURL translatorURL ( "http://translate.google.com/translate_t"); TQString body = KURL::encode_string( msg ); TQString lp = from + "|" + to; TQCString postData = TQString( "text=" + body + "&langpair=" + lp ).utf8(); TQString gurl = "http://translate.google.com/translate_t?text=" + body +"&langpair=" + lp; kdDebug(14308) << k_funcinfo << " URL: " << gurl << endl; KURL geturl ( gurl ); TDEIO::TransferJob *job = TDEIO::get( geturl, false, true ); //job = TDEIO::http_post( translatorURL, postData, true ); //job->addMetaData( "content-type", "application/x-www-form-urlencoded" ); //job->addMetaData( "referrer", "http://www.google.com" ); TQObject::connect( job, TQT_SIGNAL( data( TDEIO::Job *, const TQByteArray & ) ), this, TQT_SLOT( slotDataReceived( TDEIO::Job *, const TQByteArray & ) ) ); TQObject::connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ), this, TQT_SLOT( slotJobDone( TDEIO::Job * ) ) ); // TDEIO is async and we use a sync API, so use the processEvents hack to work around that // FIXME: We need to make the libkopete API async to get rid of this processEvents. // It often causes crashes in the code. - Martijn while ( !m_completed[ job ] ) tqApp->processEvents(); TQString data = TQString::fromLatin1( m_data[ job ] ); // After hacks, we need to clean m_data.remove( job ); m_completed.remove( job ); // kdDebug( 14308 ) << k_funcinfo << "Google response:"<< endl << data << endl; // TQRegExp re( "" ); TQRegExp re( ""); re.setMinimal( true ); re.search( data ); return re.cap( 1 ); } TQString TranslatorPlugin::babelTranslateMessage( const TQString &msg, const TQString &from, const TQString &to ) { TQString body = KURL::encode_string( msg); TQString lp = from + "_" + to; TQString gurl = "http://babelfish.altavista.com/babelfish/tr?enc=utf8&doit=done&tt=urltext&urltext=" + body + "&lp=" + lp; KURL geturl ( gurl ); kdDebug( 14308 ) << k_funcinfo << "URL: " << gurl << endl; TDEIO::TransferJob *job = TDEIO::get( geturl, false, true ); TQObject::connect( job, TQT_SIGNAL( data( TDEIO::Job *, const TQByteArray & ) ), this, TQT_SLOT( slotDataReceived( TDEIO::Job *, const TQByteArray & ) ) ); TQObject::connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ), this, TQT_SLOT( slotJobDone( TDEIO::Job * ) ) ); // TDEIO is async and we use a sync API, so use the processEvents hack to work around that // FIXME: We need to make the libkopete API async to get rid of this processEvents. // It often causes crashes in the code. - Martijn while ( !m_completed[ job ] ) tqApp->processEvents(); TQString data = TQString::fromUtf8( m_data[ job ] ); // After hacks, we need to clean m_data.remove( job ); m_completed.remove( job ); //kdDebug( 14308 ) << k_funcinfo << "Babelfish response: " << endl << data << endl; // TQRegExp re( "
(.*)(.*)
" ); re.setMinimal( true ); re.search( data ); return re.cap( 1 ); } void TranslatorPlugin::sendTranslation( Kopete::Message &msg, const TQString &translated ) { if ( translated.isEmpty() ) { kdWarning( 14308 ) << k_funcinfo << "Translated text is empty" << endl; return; } TranslateMode mode = DontTranslate; switch ( msg.direction() ) { case Kopete::Message::Outbound: mode = TranslateMode( m_outgoingMode ); break; case Kopete::Message::Inbound: mode = TranslateMode( m_incomingMode ); break; default: kdWarning( 14308 ) << k_funcinfo << "Can't determine if it is an incoming or outgoing message" << endl; }; switch ( mode ) { case JustTranslate: msg.setBody( translated, msg.format() ); break; case ShowOriginal: msg.setBody( i18n( "%2 \nAuto Translated: \n%1" ).arg( translated, msg.plainBody() ), msg.format() ); break; case ShowDialog: { TranslatorDialog *d = new TranslatorDialog( translated ); d->exec(); msg.setBody( d->translatedText(), msg.format() ); delete d; break; } case DontTranslate: default: //do nothing break; }; } void TranslatorPlugin::slotDataReceived ( TDEIO::Job *job, const TQByteArray &data ) { m_data[ job ] += TQCString( data, data.size() + 1 ); } void TranslatorPlugin::slotJobDone ( TDEIO::Job *job ) { m_completed[ job ] = true; TQObject::disconnect( job, TQT_SIGNAL( data( TDEIO::Job *, const TQByteArray & ) ), this, TQT_SLOT( slotDataReceived( TDEIO::Job *, const TQByteArray & ) ) ); TQObject::disconnect( job, TQT_SIGNAL( result( TDEIO::Job * ) ), this, TQT_SLOT( slotJobDone( TDEIO::Job * ) ) ); } void TranslatorPlugin::slotSetLanguage() { Kopete::MetaContact *m = Kopete::ContactList::self()->selectedMetaContacts().first(); if( m && m_actionLanguage ) m->setPluginData( this, "languageKey", m_languages->languageKey( m_actionLanguage->currentItem() ) ); } #include "translatorplugin.moc"