Support for Forvo online pronunciations added.

This commit is contained in:
Konstantin Isakov 2010-06-13 00:16:35 +04:00
parent c33fa2c9cc
commit 9e43f86feb
16 changed files with 1089 additions and 5 deletions

View file

@ -338,6 +338,47 @@ div.sdct_x
clear:both;
}
/************* Forvo **************/
.forvo_headword
{
margin-top: 15px;
margin-bottom: 10px;
font-weight: bold;
font-size: 15px;
}
/* A table which contains a play icon and information about the sound */
.forvo_play
{
margin-top: 8px;
margin-left: 8px;
}
.forvo_user
{
text-decoration: none;
color: green;
}
.forvo_location
{
color: grey;
}
.forvo_positive_votes
{
font-weight: bold;
color: green;
}
.forvo_negative_votes
{
font-weight: bold;
color: red;
}
/************* MediaWiki articles *****************
The following consist of excerpts from different .css files edited
with a .mwiki prepended to each record.

View file

@ -433,6 +433,19 @@ Class load() throw( exError )
}
}
QDomNode forvo = root.namedItem( "forvo" );
if ( !forvo.isNull() )
{
applyBoolOption( c.forvo.enable,
forvo.namedItem( "enable" ) );
c.forvo.apiKey = forvo.namedItem( "apiKey" ).toElement().text();
c.forvo.languageCodes = forvo.namedItem( "languageCodes" ).toElement().text();
}
else
c.forvo.languageCodes = "en, ru"; // Default demo values
QDomNode mws = root.namedItem( "mediawikis" );
if ( !mws.isNull() )
@ -783,6 +796,25 @@ void save( Class const & c ) throw( exError )
romaji.appendChild( opt );
}
{
// Forvo
QDomElement forvo = dd.createElement( "forvo" );
root.appendChild( forvo );
QDomElement opt = dd.createElement( "enable" );
opt.appendChild( dd.createTextNode( c.forvo.enable ? "1":"0" ) );
forvo.appendChild( opt );
opt = dd.createElement( "apiKey" );
opt.appendChild( dd.createTextNode( c.forvo.apiKey ) );
forvo.appendChild( opt );
opt = dd.createElement( "languageCodes" );
opt.appendChild( dd.createTextNode( c.forvo.languageCodes ) );
forvo.appendChild( opt );
}
{
QDomElement mws = dd.createElement( "mediawikis" );
root.appendChild( mws );

View file

@ -278,6 +278,25 @@ struct Transliteration
{}
};
struct Forvo
{
bool enable;
QString apiKey;
QString languageCodes;
Forvo(): enable( false )
{}
bool operator == ( Forvo const & other ) const
{ return enable == other.enable &&
apiKey == other.apiKey &&
languageCodes == other.languageCodes;
}
bool operator != ( Forvo const & other ) const
{ return ! operator == ( other ); }
};
/// Dictionaries which are temporarily disabled via the dictionary bar.
typedef QSet< QString > MutedDictionaries;
@ -293,6 +312,7 @@ struct Class
WebSites webSites;
Hunspell hunspell;
Transliteration transliteration;
Forvo forvo;
unsigned lastMainGroupId; // Last used group in main window
unsigned lastPopupGroupId; // Last used group in popup window

298
country.cc Normal file
View file

@ -0,0 +1,298 @@
/* This file is (c) 2008-2010 Konstantin Isakov <ikm@users.berlios.de>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#include "country.hh"
#include "folding.hh"
#include <QtCore>
#include "wstring_qt.hh"
namespace Country {
namespace
{
struct Database: public QMap< QString, QString >
{
static Database const & instance()
{ static Database db; return db; }
private:
Database();
void addCountry( QString const & country, QString const & code )
{
(*this)[ gd::toQString( Folding::apply( gd::toWString( country ) ) ) ] = code.toLower();
}
};
Database::Database()
{
addCountry( "Afghanistan", "AF" );
addCountry( "Aland Islands", "AX" );
addCountry( "Albania", "AL" );
addCountry( "Algeria", "DZ" );
addCountry( "American Samoa", "AS" );
addCountry( "Andorra", "AD" );
addCountry( "Angola", "AO" );
addCountry( "Anguilla", "AI" );
addCountry( "Antarctica", "AQ" );
addCountry( "Antigua and Barbuda", "AG" );
addCountry( "Argentina", "AR" );
addCountry( "Armenia", "AM" );
addCountry( "Aruba", "AW" );
addCountry( "Australia", "AU" );
addCountry( "Austria", "AT" );
addCountry( "Azerbaijan", "AZ" );
addCountry( "Bahamas", "BS" );
addCountry( "Bahrain", "BH" );
addCountry( "Bangladesh", "BD" );
addCountry( "Barbados", "BB" );
addCountry( "Belarus", "BY" );
addCountry( "Belgium", "BE" );
addCountry( "Belize", "BZ" );
addCountry( "Benin", "BJ" );
addCountry( "Bermuda", "BM" );
addCountry( "Bhutan", "BT" );
addCountry( "Bolivia, Plurinational State of", "BO" );
addCountry( "Bolivia", "BO" );
addCountry( "Bosnia and Herzegovina", "BA" );
addCountry( "Botswana", "BW" );
addCountry( "Bouvet Island", "BV" );
addCountry( "Brazil", "BR" );
addCountry( "British Indian Ocean Territory", "IO" );
addCountry( "Brunei Darussalam", "BN" );
addCountry( "Bulgaria", "BG" );
addCountry( "Burkina Faso", "BF" );
addCountry( "Burundi", "BI" );
addCountry( "Cambodia", "KH" );
addCountry( "Cameroon", "CM" );
addCountry( "Canada", "CA" );
addCountry( "Cape Verde", "CV" );
addCountry( "Cayman Islands", "KY" );
addCountry( "Central African Republic", "CF" );
addCountry( "Chad", "TD" );
addCountry( "Chile", "CL" );
addCountry( "China", "CN" );
addCountry( "Christmas Island", "CX" );
addCountry( "Cocos (Keeling) Islands", "CC" );
addCountry( "Colombia", "CO" );
addCountry( "Comoros", "KM" );
addCountry( "Congo", "CG" );
addCountry( "Congo, the Democratic Republic of the", "CD" );
addCountry( "Cook Islands", "CK" );
addCountry( "Costa Rica", "CR" );
addCountry( "Cote d'Ivoire", "CI" );
addCountry( "Croatia", "HR" );
addCountry( "Cuba", "CU" );
addCountry( "Cyprus", "CY" );
addCountry( "Czech Republic", "CZ" );
addCountry( "Denmark", "DK" );
addCountry( "Djibouti", "DJ" );
addCountry( "Dominica", "DM" );
addCountry( "Dominican Republic", "DO" );
addCountry( "Ecuador", "EC" );
addCountry( "Egypt", "EG" );
addCountry( "El Salvador", "SV" );
addCountry( "Equatorial Guinea", "GQ" );
addCountry( "Eritrea", "ER" );
addCountry( "Estonia", "EE" );
addCountry( "Ethiopia", "ET" );
addCountry( "Falkland Islands (Malvinas)", "FK" );
addCountry( "Faroe Islands", "FO" );
addCountry( "Fiji", "FJ" );
addCountry( "Finland", "FI" );
addCountry( "France", "FR" );
addCountry( "French Guiana", "GF" );
addCountry( "French Polynesia", "PF" );
addCountry( "French Southern Territories", "TF" );
addCountry( "Gabon", "GA" );
addCountry( "Gambia", "GM" );
addCountry( "Georgia", "GE" );
addCountry( "Germany", "DE" );
addCountry( "Ghana", "GH" );
addCountry( "Gibraltar", "GI" );
addCountry( "Greece", "GR" );
addCountry( "Greenland", "GL" );
addCountry( "Grenada", "GD" );
addCountry( "Guadeloupe", "GP" );
addCountry( "Guam", "GU" );
addCountry( "Guatemala", "GT" );
addCountry( "Guernsey", "GG" );
addCountry( "Guinea", "GN" );
addCountry( "Guinea-Bissau", "GW" );
addCountry( "Guyana", "GY" );
addCountry( "Haiti", "HT" );
addCountry( "Heard Island and McDonald Islands", "HM" );
addCountry( "Holy See (Vatican City State)", "VA" );
addCountry( "Honduras", "HN" );
addCountry( "Hong Kong", "HK" );
addCountry( "Hungary", "HU" );
addCountry( "Iceland", "IS" );
addCountry( "India", "IN" );
addCountry( "Indonesia", "ID" );
addCountry( "Iran, Islamic Republic of", "IR" );
addCountry( "Iran", "IR" );
addCountry( "Iraq", "IQ" );
addCountry( "Ireland", "IE" );
addCountry( "Isle of Man", "IM" );
addCountry( "Israel", "IL" );
addCountry( "Italy", "IT" );
addCountry( "Jamaica", "JM" );
addCountry( "Japan", "JP" );
addCountry( "Jersey", "JE" );
addCountry( "Jordan", "JO" );
addCountry( "Kazakhstan", "KZ" );
addCountry( "Kenya", "KE" );
addCountry( "Kiribati", "KI" );
addCountry( "Korea, Democratic People's Republic of", "KP" );
addCountry( "Korea, Republic of", "KR" );
addCountry( "Korea", "KR" );
addCountry( "Kuwait", "KW" );
addCountry( "Kyrgyzstan", "KG" );
addCountry( "Lao People's Democratic Republic", "LA" );
addCountry( "Latvia", "LV" );
addCountry( "Lebanon", "LB" );
addCountry( "Lesotho", "LS" );
addCountry( "Liberia", "LR" );
addCountry( "Libyan Arab Jamahiriya", "LY" );
addCountry( "Liechtenstein", "LI" );
addCountry( "Lithuania", "LT" );
addCountry( "Luxembourg", "LU" );
addCountry( "Macao", "MO" );
addCountry( "Macedonia, the former Yugoslav Republic of", "MK" );
addCountry( "Macedonia", "MK" );
addCountry( "Madagascar", "MG" );
addCountry( "Malawi", "MW" );
addCountry( "Malaysia", "MY" );
addCountry( "Maldives", "MV" );
addCountry( "Mali", "ML" );
addCountry( "Malta", "MT" );
addCountry( "Marshall Islands", "MH" );
addCountry( "Martinique", "MQ" );
addCountry( "Mauritania", "MR" );
addCountry( "Mauritius", "MU" );
addCountry( "Mayotte", "YT" );
addCountry( "Mexico", "MX" );
addCountry( "Micronesia, Federated States of", "FM" );
addCountry( "Micronesia", "FM" );
addCountry( "Moldova, Republic of", "MD" );
addCountry( "Moldova", "MD" );
addCountry( "Monaco", "MC" );
addCountry( "Mongolia", "MN" );
addCountry( "Montenegro", "ME" );
addCountry( "Montserrat", "MS" );
addCountry( "Morocco", "MA" );
addCountry( "Mozambique", "MZ" );
addCountry( "Myanmar", "MM" );
addCountry( "Namibia", "NA" );
addCountry( "Nauru", "NR" );
addCountry( "Nepal", "NP" );
addCountry( "Netherlands", "NL" );
addCountry( "Netherlands Antilles", "AN" );
addCountry( "New Caledonia", "NC" );
addCountry( "New Zealand", "NZ" );
addCountry( "Nicaragua", "NI" );
addCountry( "Niger", "NE" );
addCountry( "Nigeria", "NG" );
addCountry( "Niue", "NU" );
addCountry( "Norfolk Island", "NF" );
addCountry( "Northern Mariana Islands", "MP" );
addCountry( "Norway", "NO" );
addCountry( "Oman", "OM" );
addCountry( "Pakistan", "PK" );
addCountry( "Palau", "PW" );
addCountry( "Palestinian Territory, Occupied", "PS" );
addCountry( "Panama", "PA" );
addCountry( "Papua New Guinea", "PG" );
addCountry( "Paraguay", "PY" );
addCountry( "Peru", "PE" );
addCountry( "Philippines", "PH" );
addCountry( "Pitcairn", "PN" );
addCountry( "Poland", "PL" );
addCountry( "Portugal", "PT" );
addCountry( "Puerto Rico", "PR" );
addCountry( "Qatar", "QA" );
addCountry( "Reunion", "RE" );
addCountry( "Romania", "RO" );
addCountry( "Russian Federation", "RU" );
addCountry( "Russia", "RU" );
addCountry( "Rwanda", "RW" );
addCountry( "Saint Barthélemy", "BL" );
addCountry( "Saint Helena, Ascension and Tristan da Cunha", "SH" );
addCountry( "Saint Kitts and Nevis", "KN" );
addCountry( "Saint Lucia", "LC" );
addCountry( "Saint Martin (French part)", "MF" );
addCountry( "Saint Pierre and Miquelon", "PM" );
addCountry( "Saint Vincent and the Grenadines", "VC" );
addCountry( "Samoa", "WS" );
addCountry( "San Marino", "SM" );
addCountry( "Sao Tome and Principe", "ST" );
addCountry( "Saudi Arabia", "SA" );
addCountry( "Senegal", "SN" );
addCountry( "Serbia", "RS" );
addCountry( "Seychelles", "SC" );
addCountry( "Sierra Leone", "SL" );
addCountry( "Singapore", "SG" );
addCountry( "Slovakia", "SK" );
addCountry( "Slovenia", "SI" );
addCountry( "Solomon Islands", "SB" );
addCountry( "Somalia", "SO" );
addCountry( "South Africa", "ZA" );
addCountry( "South Georgia and the South Sandwich Islands", "GS" );
addCountry( "Spain", "ES" );
addCountry( "Sri Lanka", "LK" );
addCountry( "Sudan", "SD" );
addCountry( "Suriname", "SR" );
addCountry( "Svalbard and Jan Mayen", "SJ" );
addCountry( "Swaziland", "SZ" );
addCountry( "Sweden", "SE" );
addCountry( "Switzerland", "CH" );
addCountry( "Syrian Arab Republic", "SY" );
addCountry( "Taiwan, Province of China", "TW" );
addCountry( "Tajikistan", "TJ" );
addCountry( "Tanzania, United Republic of", "TZ" );
addCountry( "Tanzania", "TZ" );
addCountry( "Thailand", "TH" );
addCountry( "Timor-Leste", "TL" );
addCountry( "Togo", "TG" );
addCountry( "Tokelau", "TK" );
addCountry( "Tonga", "TO" );
addCountry( "Trinidad and Tobago", "TT" );
addCountry( "Tunisia", "TN" );
addCountry( "Turkey", "TR" );
addCountry( "Turkmenistan", "TM" );
addCountry( "Turks and Caicos Islands", "TC" );
addCountry( "Tuvalu", "TV" );
addCountry( "Uganda", "UG" );
addCountry( "Ukraine", "UA" );
addCountry( "United Arab Emirates", "AE" );
addCountry( "United Kingdom", "GB" );
addCountry( "United States", "US" );
addCountry( "United States Minor Outlying Islands", "UM" );
addCountry( "Uruguay", "UY" );
addCountry( "Uzbekistan", "UZ" );
addCountry( "Vanuatu", "VU" );
addCountry( "Venezuela, Bolivarian Republic of", "VE" );
addCountry( "Venezuela", "VE" );
addCountry( "Viet Nam", "VN" );
addCountry( "Virgin Islands, British", "VG" );
addCountry( "Virgin Islands, U.S.", "VI" );
addCountry( "Wallis and Futuna", "WF" );
addCountry( "Western Sahara", "EH" );
addCountry( "Yemen", "YE" );
addCountry( "Zambia", "ZM" );
addCountry( "Zimbabwe", "ZW" );
}
}
QString englishNametoIso2( QString const & name )
{
Database::const_iterator i = Database::instance().find( gd::toQString( Folding::apply( gd::toWString( name ) ) ) );
if ( i == Database::instance().end() )
return QString();
else
return i.value();
}
}

14
country.hh Normal file
View file

@ -0,0 +1,14 @@
#ifndef COUNTRY_HH
#define COUNTRY_HH
#include <QString>
namespace Country {
/// Attempts converting the given country name, in english, to its iso-3166-1
/// 2-letter code. If fails, empty string is returned.
QString englishNametoIso2( QString const & );
}
#endif // COUNTRY_HH

View file

@ -16,7 +16,7 @@ EditDictionaries::EditDictionaries( QWidget * parent, Config::Class & cfg_,
dictNetMgr( dictNetMgr_ ),
origCfg( cfg ),
sources( this, cfg.paths, cfg.soundDirs, cfg.hunspell, cfg.transliteration,
cfg.mediawikis, cfg.webSites ),
cfg.forvo, cfg.mediawikis, cfg.webSites ),
orderAndProps( new OrderAndProps( this, cfg.dictionaryOrder, cfg.inactiveDictionaries,
dictionaries ) ),
groups( new Groups( this, dictionaries, cfg.groups, orderAndProps->getCurrentDictionaryOrder() ) ),
@ -138,6 +138,7 @@ bool EditDictionaries::isSourcesChanged() const
sources.getSoundDirs() != cfg.soundDirs ||
sources.getHunspell() != cfg.hunspell ||
sources.getTransliteration() != cfg.transliteration ||
sources.getForvo() != cfg.forvo ||
sources.getMediaWikis() != cfg.mediawikis ||
sources.getWebSites() != cfg.webSites;
}
@ -154,6 +155,7 @@ void EditDictionaries::acceptChangedSources( bool rebuildGroups )
cfg.soundDirs = sources.getSoundDirs();
cfg.hunspell = sources.getHunspell();
cfg.transliteration = sources.getTransliteration();
cfg.forvo = sources.getForvo();
cfg.mediawikis = sources.getMediaWikis();
cfg.webSites = sources.getWebSites();

414
forvo.cc Normal file
View file

@ -0,0 +1,414 @@
/* This file is (c) 2008-2010 Konstantin Isakov <ikm@users.berlios.de>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#include "forvo.hh"
#include "wstring_qt.hh"
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QUrl>
#include <QtXml>
#include <list>
#include "audiolink.hh"
#include "htmlescape.hh"
#include "country.hh"
#include "language.hh"
#include "langcoder.hh"
namespace Forvo {
using namespace Dictionary;
namespace {
class ForvoDictionary: public Dictionary::Class
{
string name;
QString apiKey, languageCode;
QNetworkAccessManager & netMgr;
public:
ForvoDictionary( string const & id, string const & name_,
QString const & apiKey_,
QString const & languageCode_,
QNetworkAccessManager & netMgr_ ):
Dictionary::Class( id, vector< string >() ),
name( name_ ),
apiKey( apiKey_ ),
languageCode( languageCode_ ),
netMgr( netMgr_ )
{
}
virtual string getName() throw()
{ return name; }
virtual map< Property, string > getProperties() throw()
{ return map< Property, string >(); }
virtual unsigned long getArticleCount() throw()
{ return 0; }
virtual unsigned long getWordCount() throw()
{ return 0; }
virtual QIcon getIcon() throw();
virtual sptr< WordSearchRequest > prefixMatch( wstring const & word,
unsigned long /*maxResults*/ ) throw( std::exception )
{
// Dummy
sptr< WordSearchRequestInstant > sr = new WordSearchRequestInstant;
sr->getMatches().push_back( WordMatch( word, 1 ) );
return sr;
}
virtual sptr< DataRequest > getArticle( wstring const &, vector< wstring > const & alts,
wstring const & )
throw( std::exception );
};
class ForvoArticleRequest: public ForvoDataRequestSlots
{
typedef std::list< std::pair< sptr< QNetworkReply >, bool > > NetReplies;
NetReplies netReplies;
QString word, apiKey, languageCode;
string dictionaryId;
public:
ForvoArticleRequest( wstring const & word, vector< wstring > const & alts,
QString const & apiKey_,
QString const & languageCode_,
string const & dictionaryId_,
QNetworkAccessManager & mgr );
virtual void cancel();
private:
void addQuery( QNetworkAccessManager & mgr, wstring const & word );
virtual void requestFinished( QNetworkReply * );
};
void ForvoArticleRequest::cancel()
{
finish();
}
ForvoArticleRequest::ForvoArticleRequest( wstring const & str,
vector< wstring > const & /*alts*/,
QString const & apiKey_,
QString const & languageCode_,
string const & dictionaryId_,
QNetworkAccessManager & mgr ):
word( gd::toQString( str ) ), apiKey( apiKey_ ),
languageCode( languageCode_ ), dictionaryId( dictionaryId_ )
{
connect( &mgr, SIGNAL( finished( QNetworkReply * ) ),
this, SLOT( requestFinished( QNetworkReply * ) ),
Qt::QueuedConnection );
addQuery( mgr, str );
// Don't do alts for now -- the api onlu allows 1000 requests a day a key
#if 0
for( unsigned x = 0; x < alts.size(); ++x )
addQuery( mgr, alts[ x ] );
#endif
}
void ForvoArticleRequest::addQuery( QNetworkAccessManager & mgr,
wstring const & str )
{
printf( "Requesting article %ls\n", str.c_str() );
if ( apiKey.simplified().isEmpty() )
{
// Use the default api key. That's the key I have just registered myself.
// It has a limit of 1000 requests a day, and may also get banned in the
// future. Can't do much about it. Get your own key, it is simple.
apiKey = "5efa5d045a16d10ad9c4705bd5d8e56a";
}
QUrl reqUrl = QUrl::fromEncoded(
QString( "http://apifree.forvo.com/key/%1/format/xml/action/word-pronunciations/word/%2/language/%3" )
.arg( apiKey ).arg( QString::fromAscii( QUrl::toPercentEncoding( gd::toQString( str ) ) ) ).arg( languageCode ).toUtf8() );
printf( "req: %s\n", reqUrl.toEncoded().data() );
sptr< QNetworkReply > netReply = mgr.get( QNetworkRequest( reqUrl ) );
netReplies.push_back( std::make_pair( netReply, false ) );
}
void ForvoArticleRequest::requestFinished( QNetworkReply * r )
{
printf( "Finished.\n" );
if ( isFinished() ) // Was cancelled
return;
// Find this reply
bool found = false;
for( NetReplies::iterator i = netReplies.begin(); i != netReplies.end(); ++i )
{
if ( i->first.get() == r )
{
i->second = true; // Mark as finished
found = true;
break;
}
}
if ( !found )
{
// Well, that's not our reply, don't do anything
return;
}
bool updated = false;
for( ; netReplies.size() && netReplies.front().second; netReplies.pop_front() )
{
sptr< QNetworkReply > netReply = netReplies.front().first;
if ( netReply->error() == QNetworkReply::NoError )
{
QDomDocument dd;
QString errorStr;
int errorLine, errorColumn;
if ( !dd.setContent( netReply.get(), false, &errorStr, &errorLine, &errorColumn ) )
{
setErrorString( QString( tr( "XML parse error: %1 at %2,%3" ).
arg( errorStr ).arg( errorLine ).arg( errorColumn ) ) );
}
else
{
printf( "%s\n", dd.toByteArray().data() );
QDomNode items = dd.namedItem( "items" );
if ( !items.isNull() )
{
QDomNodeList nl = items.toElement().elementsByTagName( "item" );
if ( nl.count() )
{
string articleBody;
articleBody += "<div class='forvo_headword'>";
articleBody += Html::escape( word.toUtf8().data() );
articleBody += "</div>";
articleBody += "<table class=\"forvo_play\">";
for( unsigned x = 0; x < nl.length(); ++x )
{
QDomElement item = nl.item( x ).toElement();
QDomNode mp3 = item.namedItem( "pathmp3" );
if ( !mp3.isNull() )
{
articleBody += "<tr>";
QUrl url( mp3.toElement().text() );
string ref = string( "\"" ) + url.toEncoded().data() + "\"";
articleBody += addAudioLink( ref, dictionaryId ).c_str();
bool isMale = ( item.namedItem( "sex" ).toElement().text().toLower() != "f" );
QString user = item.namedItem( "username" ).toElement().text();
QString country = item.namedItem( "country" ).toElement().text();
string userProfile = string( "http://www.forvo.com/user/" ) +
QUrl::toPercentEncoding( user ).data() + "/";
int totalVotes = item.namedItem( "num_votes" ).toElement().text().toInt();
int positiveVotes = item.namedItem( "num_positive_votes" ).toElement().text().toInt();
int negativeVotes = totalVotes - positiveVotes;
string votes;
if ( positiveVotes || negativeVotes )
{
votes += " ";
if ( positiveVotes )
{
votes += "<span class='forvo_positive_votes'>+";
votes += QByteArray::number( positiveVotes ).data();
votes += "</span>";
}
if ( negativeVotes )
{
if ( positiveVotes )
votes += " ";
votes += "<span class='forvo_negative_votes'>-";
votes += QByteArray::number( negativeVotes ).data();
votes += "</span>";
}
}
string addTime =
tr( "Added %1" ).arg( item.namedItem( "addtime" ).toElement().text() ).toUtf8().data();
articleBody += "<td><a href=" + ref + " title=\"" + Html::escape( addTime ) + "\"><img src=\"qrcx://localhost/icons/playsound.png\" border=\"0\" alt=\"Play\"/></a></td>";
articleBody += string( "<td>" ) + tr( "by" ).toUtf8().data() + " <a class='forvo_user' href='"
+ userProfile + "'>"
+ Html::escape( user.toUtf8().data() )
+ "</a> <span class='forvo_location'>("
+ ( isMale ? tr( "Male" ) : tr( "Female" ) ).toUtf8().data()
+ " "
+ tr( "from" ).toUtf8().data()
+ " "
+ "<img src='qrcx://localhost/flags/" + Country::englishNametoIso2( country ).toUtf8().data()
+ ".png'/> "
+ Html::escape( country.toUtf8().data() )
+ ")</span>"
+ votes
+ "</td>";
articleBody += "</tr>";
}
}
articleBody += "</table>";
Mutex::Lock _( dataMutex );
size_t prevSize = data.size();
data.resize( prevSize + articleBody.size() );
memcpy( &data.front() + prevSize, articleBody.data(), articleBody.size() );
hasAnyData = true;
updated = true;
}
}
QDomNode errors = dd.namedItem( "errors" );
if ( !errors.isNull() )
setErrorString( errors.namedItem( "error" ).toElement().text() );
}
printf( "done.\n" );
}
else
setErrorString( netReply->errorString() );
}
if ( netReplies.empty() )
finish();
else
if ( updated )
update();
}
sptr< DataRequest > ForvoDictionary::getArticle( wstring const & word,
vector< wstring > const & alts,
wstring const & )
throw( std::exception )
{
if ( word.size() > 80 )
{
// Don't make excessively large queries -- they're fruitless anyway
return new DataRequestInstant( false );
}
else
return new ForvoArticleRequest( word, alts, apiKey, languageCode, getId(),
netMgr );
}
QIcon ForvoDictionary::getIcon() throw()
{
// Experimental code to generate icon -- but the flags clutter the interface too
// much and we're better with a single icon.
#if 0
if ( languageCode.size() == 2 )
{
QString countryCode = Language::countryCodeForId( LangCoder::code2toInt( languageCode.toAscii().data() ) );
if ( countryCode.size() )
{
QImage flag( QString( ":/flags/%1.png" ).arg( countryCode.toLower() ) );
if ( !flag.isNull() )
{
QImage img( ":/icons/forvo_icon_base.png" );
{
QPainter painter( &img );
painter.drawImage( QPoint( 5, 7 ), flag );
}
return QIcon( QPixmap::fromImage( img ) );
}
}
}
#endif
return QIcon( ":/icons/forvo.png" );
}
}
vector< sptr< Dictionary::Class > > makeDictionaries(
Dictionary::Initializing &,
Config::Forvo const & forvo,
QNetworkAccessManager & mgr )
throw( std::exception )
{
vector< sptr< Dictionary::Class > > result;
if ( forvo.enable )
{
QStringList codes = forvo.languageCodes.split( ',', QString::SkipEmptyParts );
QSet< QString > usedCodes;
for( int x = 0; x < codes.size(); ++x )
{
QString code = codes[ x ].simplified();
if ( code.size() && !usedCodes.contains( code ) )
{
// Generate id
QCryptographicHash hash( QCryptographicHash::Md5 );
hash.addData( "Forvo source version 1.0" );
hash.addData( code.toUtf8() );
QString displayedCode( code.toLower() );
if ( displayedCode.size() )
displayedCode[ 0 ] = displayedCode[ 0 ].toUpper();
result.push_back(
new ForvoDictionary( hash.result().toHex().data(),
QString( "Forvo (%1)" ).arg( displayedCode ).toUtf8().data(),
forvo.apiKey, code, mgr ) );
usedCodes.insert( code );
}
}
}
return result;
}
}

36
forvo.hh Normal file
View file

@ -0,0 +1,36 @@
/* This file is (c) 2008-2010 Konstantin Isakov <ikm@users.berlios.de>
* Part of GoldenDict. Licensed under GPLv3 or later, see the LICENSE file */
#ifndef __FORVO_HH_INCLUDED__
#define __FORVO_HH_INCLUDED__
#include "dictionary.hh"
#include "config.hh"
#include <QNetworkAccessManager>
/// Support for Forvo pronunciations, based on its API.
namespace Forvo {
using std::vector;
using std::string;
vector< sptr< Dictionary::Class > > makeDictionaries(
Dictionary::Initializing &,
Config::Forvo const &,
QNetworkAccessManager & )
throw( std::exception );
/// Exposed here for moc
class ForvoDataRequestSlots: public Dictionary::DataRequest
{
Q_OBJECT
protected slots:
virtual void requestFinished( QNetworkReply * )
{}
};
}
#endif

View file

@ -141,7 +141,9 @@ HEADERS += folding.hh \
indexedzip.hh \
termination.hh \
greektranslit.hh \
webmultimediadownload.hh
webmultimediadownload.hh \
forvo.hh \
country.hh
FORMS += groups.ui \
dictgroupwidget.ui \
mainwindow.ui \
@ -220,7 +222,9 @@ SOURCES += folding.cc \
indexedzip.cc \
termination.cc \
greektranslit.cc \
webmultimediadownload.cc
webmultimediadownload.cc \
forvo.cc \
country.cc
win32 {
SOURCES += mouseover_win32/ThTypes.c
HEADERS += mouseover_win32/ThTypes.h

BIN
icons/forvo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View file

@ -16,6 +16,7 @@
#include "german.hh"
#include "greektranslit.hh"
#include "website.hh"
#include "forvo.hh"
#include <QMessageBox>
#include <QDir>
@ -220,6 +221,15 @@ void loadDictionaries( QWidget * parent, bool showInitially,
dictionaries.insert( dictionaries.end(), dicts.begin(), dicts.end() );
}
//// Forvo dictionaries
{
vector< sptr< Dictionary::Class > > dicts =
Forvo::makeDictionaries( loadDicts, cfg.forvo, dictNetMgr );
dictionaries.insert( dictionaries.end(), dicts.begin(), dicts.end() );
}
printf( "Load done\n" );
// Remove any stale index files

View file

@ -39,5 +39,6 @@
<file>article-style-st-lingvo.css</file>
<file>icons/internet.png</file>
<file>icons/icon_dsl_native.png</file>
<file>icons/forvo.png</file>
</qresource>
</RCC>

View file

@ -12,6 +12,7 @@ Sources::Sources( QWidget * parent, Config::Paths const & paths,
Config::SoundDirs const & soundDirs,
Config::Hunspell const & hunspell,
Config::Transliteration const & trs,
Config::Forvo const & forvo,
Config::MediaWikis const & mediawikis,
Config::WebSites const & webSites ): QWidget( parent ),
mediawikisModel( this, mediawikis ),
@ -60,6 +61,10 @@ Sources::Sources( QWidget * parent, Config::Paths const & paths,
ui.enableHiragana->setChecked( trs.romaji.enableHiragana );
ui.enableKatakana->setChecked( trs.romaji.enableKatakana );
ui.forvoEnabled->setChecked( forvo.enable );
ui.forvoApiKey->setText( forvo.apiKey );
ui.forvoLanguageCodes->setText( forvo.languageCodes );
if ( Config::isPortableVersion() )
{
// Paths
@ -248,6 +253,17 @@ Config::Transliteration Sources::getTransliteration() const
return tr;
}
Config::Forvo Sources::getForvo() const
{
Config::Forvo forvo;
forvo.enable = ui.forvoEnabled->isChecked();
forvo.apiKey = ui.forvoApiKey->text();
forvo.languageCodes = ui.forvoLanguageCodes->text();
return forvo;
}
////////// MediaWikisModel
MediaWikisModel::MediaWikisModel( QWidget * parent,

View file

@ -169,6 +169,7 @@ public:
Config::SoundDirs const &,
Config::Hunspell const &,
Config::Transliteration const &,
Config::Forvo const & forvo,
Config::MediaWikis const &,
Config::WebSites const & );
@ -188,6 +189,8 @@ public:
Config::Transliteration getTransliteration() const;
Config::Forvo getForvo() const;
signals:
/// Emitted when a 'Rescan' button is clicked.

View file

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>578</width>
<width>665</width>
<height>336</height>
</rect>
</property>
@ -334,6 +334,196 @@ of the appropriate groups to use them.</string>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_5">
<attribute name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/forvo.png</normaloff>:/icons/forvo.png</iconset>
</attribute>
<attribute name="title">
<string>Forvo</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_13">
<item>
<widget class="QLabel" name="label_11">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'DejaVu Sans'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;table border=&quot;0&quot; style=&quot;-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;&quot;&gt;
&lt;tr&gt;
&lt;td style=&quot;border: none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Live pronunciations from &lt;a href=&quot;http://www.forvo.com/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;forvo.com&lt;/span&gt;&lt;/a&gt;. The site allows people to record and share word pronunciations. You can listen to them from GoldenDict.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="forvoEnabled">
<property name="title">
<string>Enable pronunciations from Forvo</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_12">
<item>
<spacer name="verticalSpacer_9">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>API Key:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="forvoApiKey">
<property name="toolTip">
<string>Use of Forvo currently requires an API key. Leave this field
blank to use the default key, which may become unavailable
in the future, or register on the site to get your own key.</string>
</property>
</widget>
</item>
<item row="1" column="0">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>18</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QLabel" name="label_13">
<property name="text">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'DejaVu Sans'; font-size:9pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;table style=&quot;-qt-table-type: root; margin-top:4px; margin-bottom:4px; margin-left:4px; margin-right:4px;&quot;&gt;
&lt;tr&gt;
&lt;td style=&quot;border: none;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Get your own key &lt;a href=&quot;http://api.forvo.com/key/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;here&lt;/span&gt;&lt;/a&gt;, or leave blank to use the default one.&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Language codes (comma-separated):</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="forvoLanguageCodes">
<property name="toolTip">
<string>List of language codes you would like to have. Example: &quot;en, ru&quot;.</string>
</property>
</widget>
</item>
<item row="4" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>18</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="1">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Full list of language codes is avaiable &lt;a href=&quot;http://www.forvo.com/languages-codes/&quot;&gt;here&lt;/a&gt;.</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<spacer name="verticalSpacer_11">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_10">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>Transliteration</string>

View file

@ -46,7 +46,10 @@ void WebMultimediaDownload::replyFinished( QNetworkReply * r )
bool WebMultimediaDownload::isAudioUrl( QUrl const & url )
{
return url.scheme() == "http" && Filetype::isNameOfSound( url.path().toUtf8().data() );
// Note: we check for forvo sound links explicitly, as they don't have extensions
return url.scheme() == "http" && (
Filetype::isNameOfSound( url.path().toUtf8().data() ) || url.host() == "apifree.forvo.com" );
}
}