MetaTranslatorMessage MetaTranslator::find( const char *context, const char *sourceText, const char *comment ) const { QMap<MetaTranslatorMessage, int>::const_iterator it = mm.constFind(MetaTranslatorMessage(context, sourceText, comment, "", QString(), 0)); return (it == mm.constEnd() ? MetaTranslatorMessage() : it.key()); }
MetaTranslatorMessage MetaTranslator::find(const char *context, const char *comment, const QString &fileName, int lineNumber) const { if (lineNumber >= 0 && !fileName.isEmpty()) { MetaTranslatorMessage m; for (QMap<MetaTranslatorMessage, int>::const_iterator it = mm.constBegin(); it != mm.constEnd(); ++it) { m = it.key(); int delta = qstrcmp(m.context(), context); if (delta == 0) { delta = qstrcmp(m.comment(), comment); if (delta == 0) { delta = QString::compare(m.fileName(), fileName); if (delta == 0) { delta = m.lineNumber() - lineNumber; if (delta == 0) return m; } } } } } return MetaTranslatorMessage(); }
bool TsHandler::endElement( const QString& /* namespaceURI */, const QString& /* localName */, const QString& qName ) { if ( qName == QString("codec") || qName == QString("defaultcodec") ) { // "codec" is a pre-3.0 syntax tor->setCodec( accum.toLatin1() ); } else if ( qName == QString("name") ) { context = accum; } else if ( qName == QString("source") ) { source = accum; } else if ( qName == QString("comment") ) { if ( inMessage ) { comment = accum; } else { if ( contextIsUtf8 ) tor->insert( MetaTranslatorMessage(context.toUtf8(), ContextComment, accum.toUtf8(), QString(), 0, QStringList(), true, MetaTranslatorMessage::Unfinished) ); else tor->insert( MetaTranslatorMessage(context.toUtf8(), ContextComment, accum.toUtf8(), QString(), 0, QStringList(), false, MetaTranslatorMessage::Unfinished) ); } } 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("message") ) { if ( messageIsUtf8 ) tor->insert( MetaTranslatorMessage(context.toUtf8(), source.toUtf8(), comment.toUtf8(), m_fileName, m_lineNumber, translations, true, type, m_isPlural) ); else tor->insert( MetaTranslatorMessage(context.toUtf8(), source.toUtf8(), comment.toUtf8(), m_fileName, m_lineNumber, translations, false, type, m_isPlural) ); inMessage = false; } return true; }
void UiHandler::flush() { if ( !context.isEmpty() && !source.isEmpty() ) tor->insert( MetaTranslatorMessage(context.utf8(), source.utf8(), comment.utf8(), QString::null, TRUE) ); source.truncate( 0 ); comment.truncate( 0 ); }
void UiHandler::flush() { if ( !context.isEmpty() && !source.isEmpty() ) tor->insert( MetaTranslatorMessage(context.toUtf8(), source.toUtf8(), comment.toUtf8(), QString(fname), m_lineNumber, QStringList(), true) ); source.truncate( 0 ); comment.truncate( 0 ); }
bool TsHandler::endElement( const QString& /* namespaceURI */, const QString& /* localName */, const QString& qName ) { if ( qName == QString( "codec" ) || qName == QString( "defaultcodec" ) ) { // "codec" is a pre-3.0 syntax tor->setCodec( accum ); } else if ( qName == QString( "name" ) ) { context = accum; } else if ( qName == QString( "source" ) ) { source = accum; } else if ( qName == QString( "comment" ) ) { if ( inMessage ) { comment = accum; } else { if ( contextIsUtf8 ) tor->insert( MetaTranslatorMessage( context.utf8(), ContextComment, accum.utf8(), QString::null, TRUE, MetaTranslatorMessage::Unfinished ) ); else tor->insert( MetaTranslatorMessage( context.ascii(), ContextComment, accum.ascii(), QString::null, FALSE, MetaTranslatorMessage::Unfinished ) ); } } else if ( qName == QString( "translation" ) ) { translation = accum; } else if ( qName == QString( "message" ) ) { if ( messageIsUtf8 ) tor->insert( MetaTranslatorMessage( context.utf8(), source.utf8(), comment.utf8(), translation, TRUE, type ) ); else tor->insert( MetaTranslatorMessage( context.ascii(), source.ascii(), comment.ascii(), translation, FALSE, type ) ); inMessage = FALSE; } return TRUE; }
void merge( const MetaTranslator *tor, const MetaTranslator *virginTor, MetaTranslator *outTor, bool verbose, bool noObsolete ) { int known = 0; int neww = 0; int obsoleted = 0; int UntranslatedObsoleted = 0; int similarTextHeuristicCount = 0; TML all = tor->messages(); TML::Iterator it; outTor->setLanguageCode(tor->languageCode()); outTor->setSourceLanguageCode(tor->sourceLanguageCode()); /* The types of all the messages from the vernacular translator are updated according to the virgin translator. */ for ( it = all.begin(); it != all.end(); ++it ) { MetaTranslatorMessage::Type newType = MetaTranslatorMessage::Finished; MetaTranslatorMessage m = *it; // skip context comment if ( !QByteArray(m.sourceText()).isEmpty() ) { MetaTranslatorMessage mv = virginTor->find(m.context(), m.sourceText(), m.comment()); if ( mv.isNull() ) { mv = virginTor->find(m.context(), m.comment(), m.fileName(), m.lineNumber()); if ( mv.isNull() ) { // did not find it in the virgin, mark it as obsolete newType = MetaTranslatorMessage::Obsolete; if ( m.type() != MetaTranslatorMessage::Obsolete ) obsoleted++; } else { // Do not just accept it if its on the same line number, but different source text. // Also check if the texts are more or less similar before we consider them to represent the same message... // ### 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.setPlural(mv.isPlural()); // Mark it as unfinished. (Since the source text was changed it might require re-translating...) newType = MetaTranslatorMessage::Unfinished; ++similarTextHeuristicCount; } else { // The virgin and vernacular sourceTexts are so different that we could not find it. newType = MetaTranslatorMessage::Obsolete; if ( m.type() != MetaTranslatorMessage::Obsolete ) obsoleted++; } neww++; } } else { switch ( m.type() ) { case MetaTranslatorMessage::Finished: default: if (m.isPlural() == mv.isPlural()) { newType = MetaTranslatorMessage::Finished; } else { newType = MetaTranslatorMessage::Unfinished; } known++; break; case MetaTranslatorMessage::Unfinished: newType = MetaTranslatorMessage::Unfinished; known++; break; case MetaTranslatorMessage::Obsolete: newType = MetaTranslatorMessage::Unfinished; neww++; } // Always get the filename and linenumber info from the virgin Translator, in case it has changed location. // This should also enable us to read a file that does not have the <location> element. m.setFileName(mv.fileName()); m.setLineNumber(mv.lineNumber()); m.setPlural(mv.isPlural()); // ### why not use operator=? } if (newType == MetaTranslatorMessage::Obsolete && !m.isTranslated()) { ++UntranslatedObsoleted; } m.setType(newType); outTor->insert(m); } } /* Messages found only in the virgin translator are added to the vernacular translator. Among these are all the context comments. */ all = virginTor->messages(); for ( it = all.begin(); it != all.end(); ++it ) { MetaTranslatorMessage mv = *it; bool found = tor->contains(mv.context(), mv.sourceText(), mv.comment()); if (!found) { MetaTranslatorMessage m = tor->find(mv.context(), mv.comment(), mv.fileName(), mv.lineNumber()); if (!m.isNull()) { if (getSimilarityScore(QString(m.sourceText()), mv.sourceText()) >= textSimilarityThreshold) { found = true; } } else { found = false; } } if ( !found ) { outTor->insert( mv ); if ( !QByteArray(mv.sourceText()).isEmpty() ) neww++; } } /* The same-text heuristic handles cases where a message has an obsolete counterpart with a different context or comment. */ int sameTextHeuristicCount = applySameTextHeuristic( outTor ); /* The number heuristic handles cases where a message has an obsolete counterpart with mostly numbers differing in the source text. */ int sameNumberHeuristicCount = applyNumberHeuristic( outTor ); if ( verbose ) { int totalFound = neww + known; fprintf( stderr, " Found %d source text%s (%d new and %d already existing)\n", totalFound, totalFound == 1 ? "" : "s", neww, known); if (obsoleted) { if (noObsolete) { fprintf( stderr, " Removed %d obsolete entr%s\n", obsoleted, obsoleted == 1 ? "y" : "ies" ); } else { int total = obsoleted - UntranslatedObsoleted; fprintf( stderr, " Kept %d obsolete translation%s\n", total, total == 1 ? "" : "s" ); fprintf( stderr, " Removed %d obsolete untranslated entr%s\n", UntranslatedObsoleted, UntranslatedObsoleted == 1 ? "y" : "ies" ); } } if (sameNumberHeuristicCount) fprintf( stderr, " Number heuristic provided %d translation%s\n", sameNumberHeuristicCount, sameNumberHeuristicCount == 1 ? "" : "s" ); if (sameTextHeuristicCount) fprintf( stderr, " Same-text heuristic provided %d translation%s\n", sameTextHeuristicCount, sameTextHeuristicCount == 1 ? "" : "s" ); if (similarTextHeuristicCount) fprintf( stderr, " Similar-text heuristic provided %d translation%s\n", similarTextHeuristicCount, similarTextHeuristicCount == 1 ? "" : "s" ); } }
static void parse( MetaTranslator *tor, const char *initialContext, const char *defaultContext ) { QMap<QCString, QCString> qualifiedContexts; QStringList namespaces; QCString context; QCString text; QCString com; QCString functionContext = initialContext; QCString prefix; bool utf8 = FALSE; bool missing_Q_OBJECT = FALSE; yyTok = getToken(); while ( yyTok != Tok_Eof ) { switch ( yyTok ) { case Tok_class: /* Partial support for inlined functions. */ yyTok = getToken(); if ( yyBraceDepth == (int) namespaces.count() && yyParenDepth == 0 ) { do { /* This code should execute only once, but we play safe with impure definitions such as 'class Q_EXPORT QMessageBox', in which case 'QMessageBox' is the class name, not 'Q_EXPORT'. */ functionContext = yyIdent; yyTok = getToken(); } while ( yyTok == Tok_Ident ); while ( yyTok == Tok_Gulbrandsen ) { yyTok = getToken(); functionContext += "::"; functionContext += yyIdent; yyTok = getToken(); } if ( yyTok == Tok_Colon ) { missing_Q_OBJECT = TRUE; } else { functionContext = defaultContext; } } break; case Tok_namespace: yyTok = getToken(); if ( yyTok == Tok_Ident ) { QCString ns = yyIdent; yyTok = getToken(); if ( yyTok == Tok_LeftBrace && yyBraceDepth == (int) namespaces.count() + 1 ) namespaces.append( QString(ns) ); } break; case Tok_tr: case Tok_trUtf8: utf8 = ( yyTok == Tok_trUtf8 ); yyTok = getToken(); if ( match(Tok_LeftParen) && matchString(&text) ) { com = ""; if ( match(Tok_RightParen) || (match(Tok_Comma) && matchString(&com) && match(Tok_RightParen)) ) { if ( prefix.isNull() ) { context = functionContext; if ( !namespaces.isEmpty() ) context.prepend( (namespaces.join(QString("::")) + QString("::")).latin1() ); } else { context = prefix; } prefix = (const char *) 0; if ( qualifiedContexts.contains(context) ) context = qualifiedContexts[context]; tor->insert( MetaTranslatorMessage(context, text, com, QString::null, utf8) ); if ( lacks_Q_OBJECT.contains(context) ) { qWarning( "%s:%d: Class '%s' lacks Q_OBJECT macro", (const char *) yyFileName, yyLineNo, (const char *) context ); lacks_Q_OBJECT.remove( context ); } else { needs_Q_OBJECT.insert( context, 0 ); } } } break; case Tok_translate: utf8 = FALSE; yyTok = getToken(); if ( match(Tok_LeftParen) && matchString(&context) && match(Tok_Comma) && matchString(&text) ) { com = ""; if ( match(Tok_RightParen) || (match(Tok_Comma) && matchString(&com) && (match(Tok_RightParen) || match(Tok_Comma) && matchEncoding(&utf8) && match(Tok_RightParen))) ) tor->insert( MetaTranslatorMessage(context, text, com, QString::null, utf8) ); } break; case Tok_Q_OBJECT: missing_Q_OBJECT = FALSE; yyTok = getToken(); break; case Tok_Ident: if ( !prefix.isNull() ) prefix += "::"; prefix += yyIdent; yyTok = getToken(); if ( yyTok != Tok_Gulbrandsen ) prefix = (const char *) 0; break; case Tok_Comment: com = yyComment; com = com.simplifyWhiteSpace(); if ( com.left(sizeof(MagicComment) - 1) == MagicComment ) { com.remove( 0, sizeof(MagicComment) - 1 ); int k = com.find( ' ' ); if ( k == -1 ) { context = com; } else { context = com.left( k ); com.remove( 0, k + 1 ); tor->insert( MetaTranslatorMessage(context, "", com, QString::null, FALSE) ); } /* Provide a backdoor for people using "using namespace". See the manual for details. */ k = 0; while ( (k = context.find("::", k)) != -1 ) { qualifiedContexts.insert( context.mid(k + 2), context ); k++; } } yyTok = getToken(); break; case Tok_Arrow: yyTok = getToken(); if ( yyTok == Tok_tr || yyTok == Tok_trUtf8 ) qWarning( "%s:%d: Cannot invoke tr() like this", (const char *) yyFileName, yyLineNo ); break; case Tok_Gulbrandsen: // at top level? if ( yyBraceDepth == (int) namespaces.count() && yyParenDepth == 0 ) functionContext = prefix; yyTok = getToken(); break; case Tok_RightBrace: case Tok_Semicolon: if ( yyBraceDepth >= 0 && yyBraceDepth + 1 == (int) namespaces.count() ) namespaces.remove( namespaces.fromLast() ); if ( yyBraceDepth == (int) namespaces.count() ) { if ( missing_Q_OBJECT ) { if ( needs_Q_OBJECT.contains(functionContext) ) { qWarning( "%s:%d: Class '%s' lacks Q_OBJECT macro", (const char *) yyFileName, yyLineNo, (const char *) functionContext ); } else { lacks_Q_OBJECT.insert( functionContext, 0 ); } } functionContext = defaultContext; missing_Q_OBJECT = FALSE; } yyTok = getToken(); break; default: yyTok = getToken(); } } if ( yyBraceDepth != 0 ) fprintf( stderr, "%s:%d: Unbalanced braces in C++ code (or abuse of the C++" " preprocessor)\n", (const char *)yyFileName, yyBraceLineNo ); else if ( yyParenDepth != 0 ) fprintf( stderr, "%s:%d: Unbalanced parentheses in C++ code (or abuse of the C++" " preprocessor)\n", (const char *)yyFileName, yyParenLineNo ); }
static void parse( MetaTranslator *tor, const char *initialContext, const char *defaultContext ) { QMap<QByteArray, QByteArray> qualifiedContexts; QByteArray context; QByteArray text; QByteArray com; QByteArray functionContext = initialContext; QByteArray prefix; bool utf8 = false; yyTok = getToken(); while ( yyTok != Tok_Eof ) { switch ( yyTok ) { case Tok_class: yyTok = getToken(); functionContext = yyIdent; yyTok = getToken(); break; case Tok_tr: case Tok_trUtf8: utf8 = (yyTok == Tok_trUtf8 || (yyCodecForTr && strcmp(yyCodecForTr->name(), "UTF-8") == 0)); yyTok = getToken(); if (match(Tok_LeftParen) && matchString(&text)) { com = ""; bool plural = false; if (match(Tok_RightParen)) { // There is no comment or plural arguments. } else if (match(Tok_Comma) && matchStringOrNone(&com)) { // There is a comment argument. if (match(Tok_RightParen)) { // There is no plural argument. } else if (match(Tok_Comma)) { // There is a plural argument. plural = true; } } if (prefix.isNull()) context = defaultContext; else if (qstrcmp(prefix, "self") == 0) context = functionContext; else context = prefix; prefix = (const char *) 0; if (qualifiedContexts.contains(context)) context = qualifiedContexts[context]; if (!text.isEmpty()) { tor->insert(MetaTranslatorMessage(context, text, com, yyFileName, yyParenLineNo, QStringList(), utf8, MetaTranslatorMessage::Unfinished, plural)); } } break; case Tok_translate: utf8 = false; yyTok = getToken(); if ( match(Tok_LeftParen) && matchString(&context) && match(Tok_Comma) && matchString(&text) ) { com = ""; bool plural = false; if (!match(Tok_RightParen)) { // look for comment if ( match(Tok_Comma) && matchStringOrNone(&com)) { if (!match(Tok_RightParen)) { // look for encoding if (match(Tok_Comma)) { if (matchEncoding(&utf8)) { if (!match(Tok_RightParen)) { // look for the plural quantifier, // this can be a number, an identifier or a function call, // so for simplicity we mark it as plural if we know we have a comma instead of an // right parentheses. plural = match(Tok_Comma); } } else { // This can be a QTranslator::translate("context", "source", "comment", n) plural translation if (matchExpression() && match(Tok_RightParen)) { plural = true; } else { break; } } } else { break; } } } else { break; } } if (!text.isEmpty()) { tor->insert( MetaTranslatorMessage(context, text, com, yyFileName, yyParenLineNo, QStringList(), utf8, MetaTranslatorMessage::Unfinished, plural) ); } } break; case Tok_Ident: if ( !prefix.isNull() ) prefix += "."; prefix += yyIdent; yyTok = getToken(); if ( yyTok != Tok_Dot ) prefix = (const char *) 0; break; case Tok_Comment: com = yyComment; com = com.simplified(); if ( com.left(sizeof(MagicComment) - 1) == MagicComment ) { com.remove( 0, sizeof(MagicComment) - 1 ); int k = com.indexOf( ' ' ); if ( k == -1 ) { context = com; } else { context = com.left( k ); com.remove( 0, k + 1 ); tor->insert( MetaTranslatorMessage(context, "", com, yyFileName, yyParenLineNo, QStringList(), false) ); } } yyTok = getToken(); break; default: yyTok = getToken(); } } if ( yyParenDepth != 0 ) qWarning( "%s: Unbalanced parentheses in Python code", (const char *) yyFileName ); }
bool MetaTranslator::contains( const char *context, const char *sourceText, const char *comment ) const { return mm.find( MetaTranslatorMessage(context, sourceText, comment) ) != mm.end(); }
bool MetaTranslator::contains( const char *context, const char *sourceText, const char *comment ) const { return mm.contains(MetaTranslatorMessage(context, sourceText, comment, "", QString(), 0)); }
bool TsHandler::endElement( const QString& /* namespaceURI */, const QString& /* localName */, const QString& qName ) { if ( qName == QString("codec") || qName == QString("defaultcodec") ) { // "codec" is a pre-3.0 syntax tor->setCodec( accum.toLatin1() ); } else if ( qName == QString("name") ) { context = accum; } else if ( qName == QString("source") ) { source = accum; } else if ( qName == QString("comment") ) { if ( inMessage ) { comment = accum; } else { if ( contextIsUtf8 ) tor->insert( MetaTranslatorMessage(context.toUtf8(), ContextComment, 0, accum.toUtf8(), QString(), 0, QStringList(), true, MetaTranslatorMessage::Unfinished) ); else tor->insert( MetaTranslatorMessage(context.toAscii(), 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_translatorComment.toUtf8(), m_fileName, m_lineNumber, translations, true, type, m_isPlural) ); else tor->insert( MetaTranslatorMessage(context.toAscii(), source.toAscii(), comment.toAscii(), m_translatorComment.toAscii(), m_fileName, m_lineNumber, translations, false, type, m_isPlural) ); inMessage = false; } return true; }
static void parse( MetaTranslator *tor, const char *initialContext, const char *defaultContext ) { QMap<QCString, QCString> qualifiedContexts; QStringList namespaces; QCString context; QCString ext; QCString text; QCString comment; QCString functionContext = initialContext; QCString prefix; bool utf8 = FALSE; yyTok = getToken(); while ( yyTok != Tok_Eof ) { switch ( yyTok ) { case Tok_i18n: utf8 = FALSE; yyTok = getToken(); if ( match( Tok_LeftParen ) && ( matchString( &context ) || matchSString( &context ) ) && match( Tok_Comma ) && ( matchString( &text ) || matchSString( &text ) ) ) { if ( ( match( Tok_Comma ) && ( matchString( &comment ) || matchSString( &comment ) ) && match( Tok_RightParen ) ) == false ) { comment = ""; } tor->insert( MetaTranslatorMessage( context, text, comment, QString::null, utf8 ) ); } // else // qDebug( " --- token failed ------------" ); break; case Tok_x18n: utf8 = FALSE; yyTok = getToken(); if ( match( Tok_LeftParen ) && ( matchString( &ext ) || matchSString( &ext ) ) && match( Tok_Comma ) && ( matchString( &context ) || matchSString( &context ) ) && match( Tok_Comma ) && ( matchString( &text ) || matchSString( &text ) ) ) { if ( ( match( Tok_Comma ) && ( matchString( &comment ) || matchSString( &comment ) ) && match( Tok_RightParen ) ) == false ) { comment = ""; } tor->insert( MetaTranslatorMessage( context, text, comment, QString::null, utf8 ) ); } // else // qDebug( " --- token failed ------------" ); break; case Tok_Ident: if ( !prefix.isNull() ) prefix += "::"; prefix += yyIdent; yyTok = getToken(); if ( yyTok != Tok_Gulbrandsen ) prefix = (const char *) 0; break; case Tok_Comment: comment = yyComment; comment = comment.simplifyWhiteSpace(); yyTok = getToken(); break; case Tok_Gulbrandsen: // at top level? if ( yyBraceDepth == (int) namespaces.count() && yyParenDepth == 0 ) functionContext = prefix; yyTok = getToken(); break; case Tok_RightBrace: case Tok_Semicolon: if ( yyBraceDepth >= 0 && yyBraceDepth + 1 == (int) namespaces.count() ) namespaces.remove( namespaces.fromLast() ); if ( yyBraceDepth == (int) namespaces.count() ) { functionContext = defaultContext; } yyTok = getToken(); break; default: yyTok = getToken(); } } // if ( yyBraceDepth != 0 ) // qWarning( "%s: Unbalanced braces in PHP code", (const char *) yyFileName ); // if ( yyParenDepth != 0 ) // qWarning( "%s: Unbalanced parentheses in PHP code", (const char *) yyFileName ); }