Reputation: 825
Qt website recommends storing localizable strings right in the middle of the code like this:
fileMenu = menuBar()->addMenu(tr("&File"));
However, how should I store strings that appear in the project multiple times? Putting them in a singleton works but tr()
call means I have to initialize them separately:
struct MyLocalizableStrings
{
QString mErrorMessageFileNotFound;
QString mErrorMessageNoCanDoThis;
MyLocalizableStrings() :
mErrorMessageFileNotFound(QObject::tr("File not found.")),
mErrorMessageNoCanDoThis(QObject::tr("Operation is not possible at the moment."))
{}
};
It's clumsy and easy to mess up; I'd prefer to keep declaration and initialization together. Could of course keep a series of const char*
in a dedicated namespace but then there's a risk of forgetting the tr()
at the call site - this whole approach is seriously nonstandard.
Any better ideas?
Upvotes: 1
Views: 238
Reputation: 98425
Strings that appear multiple times and are meant to be the same need to be in one or more dedicated classes - so you almost got it right. The class needs its translation context to make it easier for translators to understand that those are common messages. You should also leverage the member initialization in C++11 to DRY:
First, let's get a base class and necessary helpers established:
#include <QCoreApplication>
template <typename Derived>
class LocalizableStringBase {
Q_DISABLE_COPY(LocalizableStringBase)
protected:
using S = QString;
static LocalizableStringBase * m_instance;
LocalizableStringBase() {
Q_ASSERT(!m_instance);
m_instance = this;
}
~LocalizableStringBase() {
m_instance = nullptr;
}
public:
static const Derived * instance() {
return static_cast<const Derived*>(m_instance);
}
void reset() {
auto self = static_cast<const Derived*>(this);
self->~Derived();
new (self) Derived();
}
};
#define DEFINE_LOCALIZABLE_STRINGS(Derived) \
template <> \
LocalizableStringBase<Derived> * LocalizableStringBase<Derived>::m_instance = {}; \
const Derived & Derived##$() { return *(Derived::instance()); }
Then, for each set of localizable strings you'd need:
// Interface
class MyLocalizableStrings : public LocalizableStringBase<MyLocalizableStrings> {
Q_DECLARE_TR_FUNCTIONS(MyLocalizableStrings)
public:
S errorMessageFileNotFound = tr("File not found.");
S errorMessageNoCanDoThis = tr("Operation is not possible at the moment.");
};
// Implementation
DEFINE_LOCALIZABLE_STRINGS(MyLocalizableStrings)
Usage:
#include <QDebug>
void test() {
qDebug() << MyLocalizableStrings$().errorMessageFileNotFound;
}
int main(int argc, char ** argv)
{
QCoreApplication app{argc, argv};
MyLocalizableStrings str1;
test();
// Change language here
str1.reset();
test();
}
This is fairly readable and there's no duplication of identifiers. It also avoids the static initialization order fiasco.
Upvotes: 1