2

In my project we want to translate the user-interface but keep the technical logs in English. I agree those two kind of messages should remain separated but sometimes we have some error messages that we want to display and log at the same time.
Using QObject::tr you get the translated string and no way to retrieve the source string.

How could I manage log of original version + display of translated version without copy/pasting ?
I am open to any suggestion, including design constraints.

ymoreau
  • 3,402
  • 1
  • 22
  • 60
  • Why not calling the display function with the string __with__ `tr` and the logging function with the same string, but __without__ tr? – scopchanov Oct 18 '17 at 12:40
  • @scopchanov, because it is inconvenient to do: you can only achieve that b copy-pasting the string two times. It won't work to put the string into some variable and use it with and without `tr` - `tr` must be used along with string *literal*, otherwise the translation simply won't work. It is due to the mechanics of Qt's translation system - the strings to be translated are extracted from the source code by `lupdate` utility which looks at string literals within `tr` and some helper macros like `QT_TR_NOOP`. – Dmitry Oct 18 '17 at 12:42
  • @Dmitry, `It won't work to put the string into some variable` != `m_originalString(str), m_localizedString(tr(str))`. – scopchanov Oct 18 '17 at 12:47
  • Ok, correction: it would only work if you wrap the original string into a special macro `QT_TR_NOOP` or one of its relatives. Which is exactly what I describe in my answer. – Dmitry Oct 18 '17 at 12:49
  • @Dmitry, So, basically we describe the same approach. Your explanation is more comprehensive though. – scopchanov Oct 18 '17 at 12:51
  • @scopchanov Well, yes but you did not mention the most necessary part - `QT_TR_NOOP` :-P – Dmitry Oct 18 '17 at 12:54
  • That's why I said - _more elaborate_. It is a positive thing ;) – scopchanov Oct 18 '17 at 12:55

1 Answers1

6

I solved this problem in one of my own projects by introducing a special helper class holding both the original string and the translated one. Here's the rough sketch of how it looks:

class LocalizedString
{
public:
    LocalizedString(const char * str) :
        m_originalString(str),
        m_localizedString(tr(str))
    {}

    const char * originalString() const { return m_originalString; }
    const QString & localizedString() const { return m_localizedString; }

private:
    const char *     m_originalString;
    QString          m_localizedString;
}

Then the code using this class works like this:

// In one place within the code:
LocalizedString errorDescription = QT_TR_NOOP("Some message which should go both to the log and to the user");
qDebug() << errorDescription.originalString();
<...>
// In some other place within the code which gets errorDescription variable from somewhere and needs to show it to the user
showErrorMessage(errorDescription.localizedString());

The main part within the approach is the use of QT_TR_NOOP macro. What it does is marking the string literal enclosed into it as the one requiring extraction during qmake step for further translation. Unlike QObject::tr, this macro does not convert the non-translated text into the translated one. If you want to access the translated text, you need to call tr manually later on - as I did in the above example within the constructor of LocalizedString.

Note that QT_TR_NOOP is designed to be used within classes i.e. the context for translation would be the name of the class inside some method of which the macro is present. If you have free standing functions or if you want to specify the context yourself, use QT_TRANSLATE_NOOP macro instead - its second argument is the translation context.

Upd.: one more tip: in my real implementation of LocalizedString it has operator<< which prints the original, non-localized string. It is convenient because this way you can just pass the object of LocalizedString class to QDebug without calling its originalString method.

Dmitry
  • 3,063
  • 2
  • 22
  • 32
  • Thx ! Just a thought after reading this. It seems like it would be more practical to inherit from QString and just add the original-string as a member. So you can use your LocalizedString directly with all Qt function expecting a QString. – ymoreau Oct 19 '17 at 11:55
  • 1
    @ymoreau, you can do so if you wish, however, you'd need to beware of potential slicing if you assign `LocalizedString` to `QString`. – Dmitry Oct 19 '17 at 12:05