diff --git a/pylupdate/fetchtr.cpp b/pylupdate/fetchtr.cpp index 8f3d138..bd0c96f 100644 --- a/pylupdate/fetchtr.cpp +++ b/pylupdate/fetchtr.cpp @@ -630,6 +630,7 @@ static void parse( MetaTranslator *tor, const char *initialContext, if (!text.isEmpty()) { tor->insert(MetaTranslatorMessage(context, text, com, + "", yyFileName, yyLineNo, QStringList(), utf8, MetaTranslatorMessage::Unfinished, plural)); @@ -678,6 +679,7 @@ static void parse( MetaTranslator *tor, const char *initialContext, if (!text.isEmpty()) { tor->insert( MetaTranslatorMessage(context, text, com, + "", yyFileName, yyLineNo, QStringList(), utf8, MetaTranslatorMessage::Unfinished, @@ -705,6 +707,7 @@ static void parse( MetaTranslator *tor, const char *initialContext, context = com.left( k ); com.remove( 0, k + 1 ); tor->insert( MetaTranslatorMessage(context, "", com, + "", yyFileName, yyLineNo, QStringList(), false) ); @@ -847,7 +850,8 @@ void UiHandler::flush() { if ( !context.isEmpty() && !source.isEmpty() ) tor->insert( MetaTranslatorMessage(context.toUtf8(), source.toUtf8(), - comment.toUtf8(), QString(fname), m_lineNumber, + comment.toUtf8(), "", + QString(fname), m_lineNumber, QStringList(), true) ); source.truncate( 0 ); comment.truncate( 0 ); diff --git a/pylupdate/merge.cpp b/pylupdate/merge.cpp index 9d0b8ef..5430480 100644 --- a/pylupdate/merge.cpp +++ b/pylupdate/merge.cpp @@ -77,7 +77,7 @@ void merge( const MetaTranslator *tor, const MetaTranslator *virginTor, MetaTran // ### The QString() cast is evil if (getSimilarityScore(QString(m.sourceText()), mv.sourceText()) >= textSimilarityThreshold) { // It is just slightly modified, assume that it is the same string - m = MetaTranslatorMessage(m.context(), mv.sourceText(), m.comment(), m.fileName(), m.lineNumber(), m.translations()); + m = MetaTranslatorMessage(m.context(), mv.sourceText(), m.comment(), m.translatorComment(), m.fileName(), m.lineNumber(), m.translations()); m.setPlural(mv.isPlural()); // Mark it as unfinished. (Since the source text was changed it might require re-translating...) diff --git a/pylupdate/metatranslator.cpp b/pylupdate/metatranslator.cpp index afa6fda..af78961 100644 --- a/pylupdate/metatranslator.cpp +++ b/pylupdate/metatranslator.cpp @@ -34,6 +34,16 @@ #include #include +/* + * Each string may contain multiple versions of the same content. Qt + * automatically splits QStrings at the Unicode 'STRING TERMINATOR' character, + * and only displays the first variant that fits in the widget's current + * geometry. This is important for translations because variants of different + * lengths may appear in the TS file, and Qt will decide which variant to + * display based on available screen space. + */ +static const uint VARIANT_DELIMITER = 0x9c; + static bool encodingIsUtf8( const QXmlAttributes& atts ) { for ( int i = 0; i < atts.length(); i++ ) { @@ -72,6 +82,7 @@ class TsHandler : public QXmlDefaultHandler QString context; QString source; QString comment; + QString m_translatorComment; QStringList translations; QString m_fileName; int m_lineNumber; @@ -109,6 +120,7 @@ bool TsHandler::startElement( const QString& /* namespaceURI */, context.truncate( 0 ); source.truncate( 0 ); comment.truncate( 0 ); + m_translatorComment.truncate( 0 ); translations.clear(); contextIsUtf8 = encodingIsUtf8( atts ); } else if ( qName == QString("message") ) { @@ -116,6 +128,7 @@ bool TsHandler::startElement( const QString& /* namespaceURI */, type = MetaTranslatorMessage::Finished; source.truncate( 0 ); comment.truncate( 0 ); + m_translatorComment.truncate( 0 ); translations.clear(); messageIsUtf8 = encodingIsUtf8( atts ); m_isPlural = atts.value(QLatin1String("numerus")).compare(QLatin1String("yes")) == 0; @@ -159,31 +172,46 @@ bool TsHandler::endElement( const QString& /* namespaceURI */, } else { if ( contextIsUtf8 ) tor->insert( MetaTranslatorMessage(context.toUtf8(), - ContextComment, + ContextComment, 0, accum.toUtf8(), QString(), 0, QStringList(), true, MetaTranslatorMessage::Unfinished) ); else tor->insert( MetaTranslatorMessage(context.toAscii(), - ContextComment, + ContextComment, 0, accum.toAscii(), QString(), 0, QStringList(), false, MetaTranslatorMessage::Unfinished) ); } + } else if ( qName == QString("translatorcomment") ) { + m_translatorComment = accum; } else if ( qName == QString("numerusform") ) { translations.append(accum); m_isPlural = true; } else if ( qName == QString("translation") ) { if (translations.isEmpty()) translations.append(accum); + } else if ( qName == QString("lengthvariant") ) { + // The DTD comments state that lengthvariant may appear within + // numerusform, but not the other way around. + if (translations.isEmpty()) + translations.append( accum ); + else { + translations.last().append( VARIANT_DELIMITER ); + translations.last().append( accum ); + } } else if ( qName == QString("message") ) { if ( messageIsUtf8 ) tor->insert( MetaTranslatorMessage(context.toUtf8(), source.toUtf8(), - comment.toUtf8(), m_fileName, m_lineNumber, + comment.toUtf8(), + m_translatorComment.toUtf8(), + m_fileName, m_lineNumber, translations, true, type, m_isPlural) ); else tor->insert( MetaTranslatorMessage(context.toAscii(), source.toAscii(), - comment.toAscii(), m_fileName, m_lineNumber, + comment.toAscii(), + m_translatorComment.toAscii(), + m_fileName, m_lineNumber, translations, false, type, m_isPlural) ); inMessage = false; } @@ -277,11 +305,12 @@ MetaTranslatorMessage::MetaTranslatorMessage() MetaTranslatorMessage::MetaTranslatorMessage( const char *context, const char *sourceText, const char *comment, + const char *translatorComment, const QString &fileName, int lineNumber, const QStringList& translations, bool utf8, Type type, bool plural ) - : TranslatorMessage( context, sourceText, comment, fileName, lineNumber, translations ), + : TranslatorMessage( context, sourceText, comment, translatorComment, fileName, lineNumber, translations ), utfeight( false ), ty( type ), m_plural(plural) { /* @@ -472,23 +501,37 @@ bool MetaTranslator::save( const QString& filename ) const t << " " << evilBytes( msg.comment(), msg.utf8() ) << "\n"; + if ( !QByteArray(msg.translatorComment()).isEmpty() ) + t << " " + << evilBytes( msg.translatorComment(), msg.utf8() ) + << "\n"; t << " "; if (msg.isPlural()) { - t << "\n"; + t << ">\n"; QLocale::Language l; QLocale::Country c; languageAndCountry(m_language, &l, &c); QStringList translns = normalizedTranslations(msg, l, c); - for (int j = 0; j < qMax(1, translns.count()); ++j) - t << " " << protect( translns.value(j).toUtf8() ) << "\n"; + for (int j = 0; j < qMax(1, translns.count()); ++j) { + const QString &transln = translns.value(j); + t << " " + << protect( transln.toUtf8() ) << "\n"; + } t << " "; } else { - t << protect( msg.translation().toUtf8() ); + if ( msg.translation().contains( VARIANT_DELIMITER ) ) + saveLengthVariants( t, " ", msg.translation() ); + else + t << ">" + << protect( msg.translation().toUtf8() ); } t << "\n"; t << " \n"; @@ -500,6 +543,18 @@ bool MetaTranslator::save( const QString& filename ) const return true; } +void MetaTranslator::saveLengthVariants(QTextStream &t, const QString &indent, + const QString &oneString) const +{ + QStringList variants = oneString.split ( VARIANT_DELIMITER ); + t << " variants=\"yes\">\n" << indent; + for (int i = 0; i < variants.count(); ++i) + t << " " + << protect( variants.value( i ).toUtf8() ) + << "\n" + << indent; +} + void MetaTranslator::languageAndCountry(const QString &languageCode, QLocale::Language *lang, QLocale::Country *country) { QLocale locale(languageCode); @@ -538,6 +593,7 @@ bool MetaTranslator::release( const QString& filename, bool verbose, QByteArray context = m.key().context(); QByteArray sourceText = m.key().sourceText(); QByteArray comment = m.key().comment(); + QByteArray translatorComment = m.key().translatorComment(); QStringList translations = m.key().translations(); if ( !ignoreUnfinished @@ -556,7 +612,7 @@ bool MetaTranslator::release( const QString& filename, bool verbose, .isNull() ) { tor.insert( m.key() ); } else { - tor.insert( TranslatorMessage(context, sourceText, "", + tor.insert( TranslatorMessage(context, sourceText, "", "", QString(), -1, translations) ); //filename and lineNumbers will be ignored from now. } @@ -587,14 +643,14 @@ void MetaTranslator::setLanguageCode(const QString &languageCode) bool MetaTranslator::contains( const char *context, const char *sourceText, const char *comment ) const { - return mm.contains(MetaTranslatorMessage(context, sourceText, comment, QString(), 0)); + return mm.contains(MetaTranslatorMessage(context, sourceText, comment, "", QString(), 0)); } MetaTranslatorMessage MetaTranslator::find( const char *context, const char *sourceText, const char *comment ) const { QMap::const_iterator it = - mm.constFind(MetaTranslatorMessage(context, sourceText, comment, QString(), 0)); + mm.constFind(MetaTranslatorMessage(context, sourceText, comment, "", QString(), 0)); return (it == mm.constEnd() ? MetaTranslatorMessage() : it.key()); } diff --git a/pylupdate/metatranslator.h b/pylupdate/metatranslator.h index 2b0fa8b..edf841b 100644 --- a/pylupdate/metatranslator.h +++ b/pylupdate/metatranslator.h @@ -42,7 +42,7 @@ class MetaTranslatorMessage : public TranslatorMessage MetaTranslatorMessage(); MetaTranslatorMessage( const char *context, const char *sourceText, - const char *comment, + const char *comment, const char *translatorComment, const QString &fileName, int lineNumber, const QStringList& translations = QStringList(), bool utf8 = false, Type type = Unfinished, @@ -118,6 +118,10 @@ class MetaTranslator private: void makeFileNamesAbsolute(const QDir &oldPath); + // A helper method to output translations with multiple variants. These + // may appear in simple translation tags or inside numerusform tags. + void saveLengthVariants(QTextStream &t, const QString &indent, + const QString &oneString) const; typedef QMap TMM; typedef QMap TMMInv; diff --git a/pylupdate/translator.cpp b/pylupdate/translator.cpp index 7a90770..f586d7b 100644 --- a/pylupdate/translator.cpp +++ b/pylupdate/translator.cpp @@ -807,20 +807,20 @@ TranslatorMessage Translator::findMessage(const char *context, const char *sourc // Either we want to find an item that matches context, sourcetext (and optionally comment) // Or we want to find an item that matches context, filename, linenumber (and optionally comment) - it = d->messages.find(TranslatorMessage(context, sourceText, comment, myFilename, myLineNumber)); + it = d->messages.find(TranslatorMessage(context, sourceText, comment, "", myFilename, myLineNumber)); if (it != d->messages.constEnd()) return it.key(); if (comment[0]) { - it = d->messages.find(TranslatorMessage(context, sourceText, "", myFilename, myLineNumber)); + it = d->messages.find(TranslatorMessage(context, sourceText, "", "", myFilename, myLineNumber)); if (it != d->messages.constEnd()) return it.key(); } - it = d->messages.find(TranslatorMessage(context, "", comment, myFilename, myLineNumber)); + it = d->messages.find(TranslatorMessage(context, "", comment, "", myFilename, myLineNumber)); if (it != d->messages.constEnd()) return it.key(); if (comment[0]) { - it = d->messages.find(TranslatorMessage(context, "", "", myFilename, myLineNumber)); + it = d->messages.find(TranslatorMessage(context, "", "", "", myFilename, myLineNumber)); if (it != d->messages.constEnd()) return it.key(); } @@ -914,10 +914,13 @@ TranslatorMessage::TranslatorMessage() TranslatorMessage::TranslatorMessage(const char * context, const char * sourceText, const char * comment, + const char * translatorComment, const QString &fileName, int lineNumber, const QStringList& translations) - : cx(context), st(sourceText), cm(comment), m_translations(translations), + : cx(context), st(sourceText), cm(comment), + m_translatorComment(translatorComment), + m_translations(translations), m_fileName(fileName), m_lineNumber(lineNumber) { // 0 means we don't know, "" means empty @@ -927,6 +930,8 @@ TranslatorMessage::TranslatorMessage(const char * context, st = ""; if (cm == (const char*)0) cm = ""; + if (m_translatorComment == (const char*)0) + m_translatorComment = ""; h = elfHash(st + cm); } @@ -936,7 +941,9 @@ TranslatorMessage::TranslatorMessage(const char * context, */ TranslatorMessage::TranslatorMessage(const TranslatorMessage & m) - : cx(m.cx), st(m.st), cm(m.cm), m_translations(m.m_translations), + : cx(m.cx), st(m.st), cm(m.cm), + m_translatorComment(m.m_translatorComment), + m_translations(m.m_translations), m_fileName(m.m_fileName), m_lineNumber(m.m_lineNumber) { h = m.h; @@ -955,6 +962,7 @@ TranslatorMessage & TranslatorMessage::operator=( cx = m.cx; st = m.st; cm = m.cm; + m_translatorComment = m.m_translatorComment; m_translations = m.m_translations; m_fileName = m.m_fileName; m_lineNumber = m.m_lineNumber; diff --git a/pylupdate/translator.h b/pylupdate/translator.h index 8b26941..d90f323 100644 --- a/pylupdate/translator.h +++ b/pylupdate/translator.h @@ -41,6 +41,7 @@ class TranslatorMessage TranslatorMessage(); TranslatorMessage(const char * context, const char * sourceText, const char * comment, + const char * translatorComment, const QString &fileName, int lineNumber, const QStringList& translations = QStringList()); @@ -52,6 +53,9 @@ class TranslatorMessage const char *context() const { return cx.isNull() ? 0 : cx.constData(); } const char *sourceText() const { return st.isNull() ? 0 : st.constData(); } const char *comment() const { return cm.isNull() ? 0 : cm.constData(); } + const char *translatorComment() const + { return m_translatorComment.isNull() + ? 0 : m_translatorComment.constData(); } inline void setTranslations(const QStringList &translations); QStringList translations() const { return m_translations; } @@ -87,6 +91,7 @@ class TranslatorMessage QByteArray cx; QByteArray st; QByteArray cm; + QByteArray m_translatorComment; QStringList m_translations; QString m_fileName; int m_lineNumber; @@ -128,11 +133,11 @@ class Translator : public QTranslator void insert(const TranslatorMessage&); inline void insert(const char *context, const char *sourceText, const QString &fileName, int lineNo, const QStringList &translations) { - insert(TranslatorMessage(context, sourceText, "", fileName, lineNo, translations)); + insert(TranslatorMessage(context, sourceText, "", "", fileName, lineNo, translations)); } void remove(const TranslatorMessage&); inline void remove(const char *context, const char *sourceText) { - remove(TranslatorMessage(context, sourceText, "", QLatin1String(""), -1)); + remove(TranslatorMessage(context, sourceText, "", "", QLatin1String(""), -1)); } bool contains(const char *context, const char *sourceText, const char * comment = 0) const; bool contains(const char *context, const char *comment, const QString &fileName, int lineNumber) const;