Qt World Summit: Register Today!

Interesting effect with tr().arg()

  • Hi all,

    I observed an interesting issue with tr().arg() a minute ago - I do have a work-around but wonder if this is actually a bug. Opinions of course welcome.

    Suppose you do the following:


    The expected outcome is "A%2AB", right? Well, the "%" in the second arg() seems to throw the replacement algorithm off, because the actual result is "ABA%3".

    My workaround is to put the "%"-containing text as the last arg(), then it works:


    It seem to me that the text in an arg should not affect how the args are treated for tr().


  • @ChrisVT
    Firstly, I can't see how this can have anything to do with tr() function. As per http://doc.qt.io/qt-5/qobject.html#tr, QObject::tr() simply returns a QString, only then does it evaluate the .arg()s, so the whole thing is simply QString::arg() calls, forget about tr().

    • Check what plain tr("%1%2%3") returns?
    • Check what QString("%1%2%3").arg("A").arg("%2A").arg("B") returns?

  • Lifetime Qt Champion


    There's a warning about that in QString's documentation

  • @ChrisVT Hi,Friend,Welcome.

    You can write some test code, to find why. It is interesting.

    Do not omit Intermediate results.

    You will clear well from following snippet:

    #include <QCoreApplication>
    #include <QtCore/QDebug>
    int main(int argc, char *argv[])
        QCoreApplication a(argc, argv);
        qDebug() << QObject::tr("%1%2%3"); /// %1%2%3
        qDebug() << QObject::tr("%1%2%3").arg("A"); /// A%2%3
        qDebug() << QObject::tr("%2%3").arg("A"); ///A%3
        qDebug() << QObject::tr("%1%2%3").arg("A").arg("%2A"); /// A%2A%3
        qDebug() << QObject::tr("%1%2%3").arg("A").arg("%2A").arg("B"); /// ABA%3
        // To replace First %, is %1=>A, result is tr(A%2%3).arg(%2A).arg(B) 
        // In Result To replace First %, is %2=>%2, result is (A%2A%3).arg(B)
        // In Result To replace First %, is %2=>B, result is ABA%3
        return a.exec();

  • I discussed in the past this and also the season why you should only use arg(QString) and not the other overloads

  • @joeQ
    I still don't get how this behaviour is anything to do with using QObject::tr(), in your examples and this thread title? I can't test because I don't use C++, but wouldn't your examples behave just the same if you used QString in place of QObject::tr? The arg()s are evaluated after the tr(), not before, aren't they?

  • Further to what I have written above, and to @VRonin 's post:

    ISTM that you should never use qString.arg().arg(). The chained args, which work on the substituted result from the previous arg() calls, are far too dangerous. [You wouldn't use C's sprint(format) with a non-literal format string for the same kind of reason, would you?]

    Like with sprintf, or repeated replaces or regular expression substitutions, you can only afford to use a substituter which works from a single, original string specifying what to do, not one affected by a previous substitution. You need a function which does multiple substitutions at the same time, not one after the other, to be safe.

    So I don't see/where is Qt's QString::argList() function, so you can go like: QString("%1%2%3")::argList(arg1, arg2, arg3), which is what you need to do?

  • @JonB Correct, this has nothing to do with tr(). This was just the way I used it in my original code, and I didn't remove it. Sorry. The problem also occurs with QString.

  • @JonB said in Interesting effect with tr().arg():

    You need a function which does multiple substitutions at the same time, not one after the other, to be safe.


    @JonB said in Interesting effect with tr().arg():

    which is what you need to do?

    Actually it's very easy to solve the problem at hand: tr("%1%2%3").arg(QStringLiteral("A"),QStringLiteral("%2A"),QStringLiteral("B"))

  • @VRonin
    .arg() takes a list of arguments?

  • @VRonin
    Yeah, I just saw that, that's what I call "cheating" :)
    Are the %numbers limited to 9 in the format string?
    Where is QString::argv(arrayOfArgs) ? ;-)

  • Thanks for all the responses - very insightful!

    @VRonin This also happens with QStrings:
    qDebug() << QString("%1%2%3").arg("A").arg("%2A").arg("B"); -> "ABA%3"
    qDebug() << QString("%1%2%3").arg(QString("A")).arg(QString("%2A")).arg(QString("B")); -> "ABA%3"
    But yes, implicit type conversion can be very hard to figure out. Been there!

    @joeB Good advice about using the arglist instead, but
    (a) this only works for QStrings, not for numerical formats, and
    (b) Qt offers the ability to use chained args which MOSTLY work.
    I was incorrectly assuming that arg(a).arg(b) should be the same as .arg(a,b) for constant QStrings a and b. I'll still say it's not all that intuitive that it isn't.

  • @ChrisVT
    I love things which "MOSTLY" work :)

    Be aware, if you are going to use "numerical formats" as you say then, as @VRonin pointed out in the post he referred to, these will not be localed. If you care about that, you must format them to localed strings in the first place, then you can pass them to the list-arg() because they are now strings.

Log in to reply