I18N: support localised local aircraft strings
Allows us to localize aircraft names/descriptions, especially for the UFO and C172
This commit is contained in:
parent
585c821bde
commit
5a11e57d0a
5 changed files with 129 additions and 26 deletions
|
@ -277,11 +277,11 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta
|
||||||
if (role >= AircraftVariantDescriptionRole) {
|
if (role >= AircraftVariantDescriptionRole) {
|
||||||
int variantIndex = role - AircraftVariantDescriptionRole;
|
int variantIndex = role - AircraftVariantDescriptionRole;
|
||||||
if (variantIndex == 0) {
|
if (variantIndex == 0) {
|
||||||
return item->description;
|
return item->name();
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_ASSERT(variantIndex < item->variants.size());
|
Q_ASSERT(variantIndex < item->variants.size());
|
||||||
return item->variants.at(variantIndex)->description;
|
return item->variants.at(variantIndex)->name();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.variant) {
|
if (state.variant) {
|
||||||
|
@ -292,11 +292,11 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == Qt::DisplayRole) {
|
if (role == Qt::DisplayRole) {
|
||||||
if (item->description.isEmpty()) {
|
if (item->name().isEmpty()) {
|
||||||
return tr("Missing description for: %1").arg(item->baseName());
|
return tr("Missing description for: %1").arg(item->baseName());
|
||||||
}
|
}
|
||||||
|
|
||||||
return item->description;
|
return item->name();
|
||||||
} else if (role == AircraftPathRole) {
|
} else if (role == AircraftPathRole) {
|
||||||
return item->path;
|
return item->path;
|
||||||
} else if (role == AircraftAuthorsRole) {
|
} else if (role == AircraftAuthorsRole) {
|
||||||
|
@ -318,7 +318,7 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta
|
||||||
}
|
}
|
||||||
return have;
|
return have;
|
||||||
} else if (role == AircraftLongDescriptionRole) {
|
} else if (role == AircraftLongDescriptionRole) {
|
||||||
return item->longDescription;
|
return item->description();
|
||||||
} else if (role == AircraftIsHelicopterRole) {
|
} else if (role == AircraftIsHelicopterRole) {
|
||||||
return item->usesHeliports;
|
return item->usesHeliports;
|
||||||
} else if (role == AircraftIsSeaplaneRole) {
|
} else if (role == AircraftIsSeaplaneRole) {
|
||||||
|
@ -564,14 +564,14 @@ QString AircraftItemModel::nameForAircraftURI(QUrl uri) const
|
||||||
|
|
||||||
const QString path = uri.toLocalFile();
|
const QString path = uri.toLocalFile();
|
||||||
if (item->path == path) {
|
if (item->path == path) {
|
||||||
return item->description;
|
return item->name();
|
||||||
}
|
}
|
||||||
|
|
||||||
// check variants too
|
// check variants too
|
||||||
for (int vr=0; vr < item->variants.size(); ++vr) {
|
for (int vr=0; vr < item->variants.size(); ++vr) {
|
||||||
auto variant = item->variants.at(vr);
|
auto variant = item->variants.at(vr);
|
||||||
if (variant->path == path) {
|
if (variant->path == path) {
|
||||||
return variant->description;
|
return variant->name();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (uri.scheme() == "package") {
|
} else if (uri.scheme() == "package") {
|
||||||
|
|
|
@ -31,17 +31,28 @@
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
#include <Main/globals.hxx>
|
|
||||||
#include <Include/version.h>
|
#include <Include/version.h>
|
||||||
|
#include <Main/globals.hxx>
|
||||||
|
#include <Main/locale.hxx>
|
||||||
|
|
||||||
#include <simgear/misc/ResourceManager.hxx>
|
#include <simgear/misc/ResourceManager.hxx>
|
||||||
#include <simgear/props/props_io.hxx>
|
#include <simgear/props/props_io.hxx>
|
||||||
#include <simgear/structure/exception.hxx>
|
#include <simgear/structure/exception.hxx>
|
||||||
|
|
||||||
static quint32 CACHE_VERSION = 12;
|
static quint32 CACHE_VERSION = 13;
|
||||||
|
|
||||||
AircraftItem::AircraftItem()
|
const std::vector<QByteArray> static_localizedStringTags = {"name", "desc"};
|
||||||
|
|
||||||
|
QDataStream& operator<<(QDataStream& ds, const AircraftItem::LocalizedStrings& ls)
|
||||||
{
|
{
|
||||||
|
ds << ls.locale << ls.strings;
|
||||||
|
return ds;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream& operator>>(QDataStream& ds, AircraftItem::LocalizedStrings& ls)
|
||||||
|
{
|
||||||
|
ds >> ls.locale >> ls.strings;
|
||||||
|
return ds;
|
||||||
}
|
}
|
||||||
|
|
||||||
AircraftItem::AircraftItem(QDir dir, QString filePath)
|
AircraftItem::AircraftItem(QDir dir, QString filePath)
|
||||||
|
@ -62,10 +73,9 @@ AircraftItem::AircraftItem(QDir dir, QString filePath)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
description = sim->getStringValue("description");
|
LocalizedStrings ls;
|
||||||
// trim the description to ensure the alphabetical sort
|
ls.locale = "en";
|
||||||
// doesn't get thrown off by leading whitespace
|
ls.strings["name"] = QString::fromStdString(sim->getStringValue("description")).trimmed();
|
||||||
description = description.trimmed();
|
|
||||||
authors = sim->getStringValue("author");
|
authors = sim->getStringValue("author");
|
||||||
|
|
||||||
if (sim->hasChild("rating")) {
|
if (sim->hasChild("rating")) {
|
||||||
|
@ -77,7 +87,7 @@ AircraftItem::AircraftItem(QDir dir, QString filePath)
|
||||||
|
|
||||||
if (sim->hasChild("long-description")) {
|
if (sim->hasChild("long-description")) {
|
||||||
// clean up any XML whitspace in the text.
|
// clean up any XML whitspace in the text.
|
||||||
longDescription = QString(sim->getStringValue("long-description")).simplified();
|
ls.strings["desc"] = QString(sim->getStringValue("long-description")).simplified();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sim->hasChild("variant-of")) {
|
if (sim->hasChild("variant-of")) {
|
||||||
|
@ -132,6 +142,65 @@ AircraftItem::AircraftItem(QDir dir, QString filePath)
|
||||||
homepageUrl = QUrl(QString::fromStdString(sim->getStringValue("urls/home-page")));
|
homepageUrl = QUrl(QString::fromStdString(sim->getStringValue("urls/home-page")));
|
||||||
supportUrl = QUrl(QString::fromStdString(sim->getStringValue("urls/support")));
|
supportUrl = QUrl(QString::fromStdString(sim->getStringValue("urls/support")));
|
||||||
wikipediaUrl = QUrl(QString::fromStdString(sim->getStringValue("urls/wikipedia")));
|
wikipediaUrl = QUrl(QString::fromStdString(sim->getStringValue("urls/wikipedia")));
|
||||||
|
|
||||||
|
_localized.push_front(ls);
|
||||||
|
readLocalizedStrings(sim);
|
||||||
|
doLocalizeStrings();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AircraftItem::readLocalizedStrings(SGPropertyNode_ptr simNode)
|
||||||
|
{
|
||||||
|
if (!simNode->hasChild("localized"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto localeNode = simNode->getChild("localized");
|
||||||
|
const auto num = localeNode->nChildren();
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
const SGPropertyNode* c = localeNode->getChild(i);
|
||||||
|
|
||||||
|
LocalizedStrings ls;
|
||||||
|
ls.locale = QString::fromStdString(c->getNameString());
|
||||||
|
if (c->hasChild("description")) {
|
||||||
|
ls.strings["name"] = QString::fromStdString(c->getStringValue("description"));
|
||||||
|
}
|
||||||
|
if (c->hasChild("long-description")) {
|
||||||
|
ls.strings["desc"] = QString::fromStdString(c->getStringValue("long-description")).simplified();
|
||||||
|
}
|
||||||
|
|
||||||
|
_localized.push_back(ls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AircraftItem::doLocalizeStrings()
|
||||||
|
{
|
||||||
|
// default strings are always at the front
|
||||||
|
_currentStrings = _localized.front().strings;
|
||||||
|
|
||||||
|
const auto lang = QString::fromStdString(globals->get_locale()->getPreferredLanguage());
|
||||||
|
// find the matching locale
|
||||||
|
auto it = std::find_if(_localized.begin(), _localized.end(), [lang](const LocalizedStrings& ls) {
|
||||||
|
return ls.locale == lang;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it == _localized.end())
|
||||||
|
return; // nothing else to do
|
||||||
|
|
||||||
|
for (auto t : static_localizedStringTags) {
|
||||||
|
if (it->strings.contains(t)) {
|
||||||
|
// copy the value we found
|
||||||
|
_currentStrings[t] = it->strings.value(t);
|
||||||
|
}
|
||||||
|
} // of strings iteration
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AircraftItem::name() const
|
||||||
|
{
|
||||||
|
return _currentStrings.value("name");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AircraftItem::description() const
|
||||||
|
{
|
||||||
|
return _currentStrings.value("desc");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString AircraftItem::baseName() const
|
QString AircraftItem::baseName() const
|
||||||
|
@ -148,7 +217,7 @@ void AircraftItem::fromDataStream(QDataStream& ds)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ds >> description >> longDescription >> authors >> variantOf >> isPrimary;
|
ds >> authors >> variantOf >> isPrimary;
|
||||||
for (int i=0; i<4; ++i) ds >> ratings[i];
|
for (int i=0; i<4; ++i) ds >> ratings[i];
|
||||||
ds >> previews;
|
ds >> previews;
|
||||||
ds >> thumbnailPath;
|
ds >> thumbnailPath;
|
||||||
|
@ -156,6 +225,9 @@ void AircraftItem::fromDataStream(QDataStream& ds)
|
||||||
ds >> needsMaintenance >> usesHeliports >> usesSeaports;
|
ds >> needsMaintenance >> usesHeliports >> usesSeaports;
|
||||||
ds >> homepageUrl >> supportUrl >> wikipediaUrl;
|
ds >> homepageUrl >> supportUrl >> wikipediaUrl;
|
||||||
ds >> tags;
|
ds >> tags;
|
||||||
|
ds >> _localized;
|
||||||
|
|
||||||
|
doLocalizeStrings();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AircraftItem::toDataStream(QDataStream& ds) const
|
void AircraftItem::toDataStream(QDataStream& ds) const
|
||||||
|
@ -165,7 +237,7 @@ void AircraftItem::toDataStream(QDataStream& ds) const
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ds << description << longDescription << authors << variantOf << isPrimary;
|
ds << authors << variantOf << isPrimary;
|
||||||
for (int i=0; i<4; ++i) ds << ratings[i];
|
for (int i=0; i<4; ++i) ds << ratings[i];
|
||||||
ds << previews;
|
ds << previews;
|
||||||
ds << thumbnailPath;
|
ds << thumbnailPath;
|
||||||
|
@ -173,6 +245,7 @@ void AircraftItem::toDataStream(QDataStream& ds) const
|
||||||
ds << needsMaintenance << usesHeliports << usesSeaports;
|
ds << needsMaintenance << usesHeliports << usesSeaports;
|
||||||
ds << homepageUrl << supportUrl << wikipediaUrl;
|
ds << homepageUrl << supportUrl << wikipediaUrl;
|
||||||
ds << tags;
|
ds << tags;
|
||||||
|
ds << _localized;
|
||||||
}
|
}
|
||||||
|
|
||||||
int AircraftItem::indexOfVariant(QUrl uri) const
|
int AircraftItem::indexOfVariant(QUrl uri) const
|
||||||
|
@ -216,6 +289,7 @@ public:
|
||||||
|
|
||||||
SGPath resolve(const std::string& aResource, SGPath& aContext) const override
|
SGPath resolve(const std::string& aResource, SGPath& aContext) const override
|
||||||
{
|
{
|
||||||
|
Q_UNUSED(aContext)
|
||||||
string_list pieces(sgPathBranchSplit(aResource));
|
string_list pieces(sgPathBranchSplit(aResource));
|
||||||
if ((pieces.size() < 3) || (pieces.front() != "Aircraft")) {
|
if ((pieces.size() < 3) || (pieces.front() != "Aircraft")) {
|
||||||
return SGPath{}; // not an Aircraft path
|
return SGPath{}; // not an Aircraft path
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
|
#include <simgear/props/props.hxx>
|
||||||
|
|
||||||
class QDataStream;
|
class QDataStream;
|
||||||
struct AircraftItem;
|
struct AircraftItem;
|
||||||
class AircraftScanThread;
|
class AircraftScanThread;
|
||||||
|
@ -38,13 +40,17 @@ typedef QSharedPointer<AircraftItem> AircraftItemPtr;
|
||||||
|
|
||||||
struct AircraftItem
|
struct AircraftItem
|
||||||
{
|
{
|
||||||
AircraftItem();
|
AircraftItem() = default;
|
||||||
|
|
||||||
AircraftItem(QDir dir, QString filePath);
|
AircraftItem(QDir dir, QString filePath);
|
||||||
|
|
||||||
// the file-name without -set.xml suffix
|
// the file-name without -set.xml suffix
|
||||||
QString baseName() const;
|
QString baseName() const;
|
||||||
|
|
||||||
|
QString name() const;
|
||||||
|
|
||||||
|
QString description() const;
|
||||||
|
|
||||||
void fromDataStream(QDataStream& ds);
|
void fromDataStream(QDataStream& ds);
|
||||||
|
|
||||||
void toDataStream(QDataStream& ds) const;
|
void toDataStream(QDataStream& ds) const;
|
||||||
|
@ -53,8 +59,7 @@ struct AircraftItem
|
||||||
|
|
||||||
bool excluded = false;
|
bool excluded = false;
|
||||||
QString path;
|
QString path;
|
||||||
QString description;
|
|
||||||
QString longDescription;
|
|
||||||
QString authors; // legacy authors data only
|
QString authors; // legacy authors data only
|
||||||
int ratings[4] = {0, 0, 0, 0};
|
int ratings[4] = {0, 0, 0, 0};
|
||||||
QString variantOf;
|
QString variantOf;
|
||||||
|
@ -72,7 +77,31 @@ struct AircraftItem
|
||||||
QUrl wikipediaUrl;
|
QUrl wikipediaUrl;
|
||||||
QUrl supportUrl;
|
QUrl supportUrl;
|
||||||
QVariant status(int variant);
|
QVariant status(int variant);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct LocalizedStrings {
|
||||||
|
QString locale;
|
||||||
|
QMap<QByteArray, QString> strings;
|
||||||
|
};
|
||||||
|
|
||||||
|
friend QDataStream& operator<<(QDataStream&, const LocalizedStrings&);
|
||||||
|
friend QDataStream& operator>>(QDataStream&, LocalizedStrings&);
|
||||||
|
|
||||||
|
using LocalizedStringsVec = QVector<LocalizedStrings>;
|
||||||
|
|
||||||
|
// store all localized strings. We need this to avoid rebuilding
|
||||||
|
// the cache when switching languages.
|
||||||
|
LocalizedStringsVec _localized;
|
||||||
|
|
||||||
|
// the resolved values for our strings, based on QLocale
|
||||||
|
// if we support dynamic switching of language, this would need to
|
||||||
|
// be flushed and re-computed
|
||||||
|
QMap<QByteArray, QString> _currentStrings;
|
||||||
|
|
||||||
|
void doLocalizeStrings();
|
||||||
|
|
||||||
|
void readLocalizedStrings(SGPropertyNode_ptr simNode);
|
||||||
};
|
};
|
||||||
|
|
||||||
class LocalAircraftCache : public QObject
|
class LocalAircraftCache : public QObject
|
||||||
|
|
|
@ -342,7 +342,7 @@ quint32 QmlAircraftInfo::numVariants() const
|
||||||
QString QmlAircraftInfo::name() const
|
QString QmlAircraftInfo::name() const
|
||||||
{
|
{
|
||||||
if (_item) {
|
if (_item) {
|
||||||
return resolveItem()->description;
|
return resolveItem()->name();
|
||||||
} else if (_package) {
|
} else if (_package) {
|
||||||
return QString::fromStdString(_package->nameForVariant(_variant));
|
return QString::fromStdString(_package->nameForVariant(_variant));
|
||||||
}
|
}
|
||||||
|
@ -353,7 +353,7 @@ QString QmlAircraftInfo::name() const
|
||||||
QString QmlAircraftInfo::description() const
|
QString QmlAircraftInfo::description() const
|
||||||
{
|
{
|
||||||
if (_item) {
|
if (_item) {
|
||||||
return resolveItem()->longDescription;
|
return resolveItem()->description();
|
||||||
} else if (_package) {
|
} else if (_package) {
|
||||||
std::string longDesc = _package->getLocalisedProp("description", _variant);
|
std::string longDesc = _package->getLocalisedProp("description", _variant);
|
||||||
return QString::fromStdString(longDesc).simplified();
|
return QString::fromStdString(longDesc).simplified();
|
||||||
|
@ -773,12 +773,12 @@ QStringList QmlAircraftInfo::variantNames() const
|
||||||
{
|
{
|
||||||
QStringList result;
|
QStringList result;
|
||||||
if (_item) {
|
if (_item) {
|
||||||
result.append(_item->description);
|
result.append(_item->name());
|
||||||
Q_FOREACH(auto v, _item->variants) {
|
Q_FOREACH(auto v, _item->variants) {
|
||||||
if (v->description.isEmpty()) {
|
if (v->name().isEmpty()) {
|
||||||
qWarning() << Q_FUNC_INFO << "missing description for " << v->path;
|
qWarning() << Q_FUNC_INFO << "missing description for " << v->path;
|
||||||
}
|
}
|
||||||
result.append(v->description);
|
result.append(v->name());
|
||||||
}
|
}
|
||||||
} else if (_package) {
|
} else if (_package) {
|
||||||
for (quint32 vindex = 0; vindex < _package->variants().size(); ++vindex) {
|
for (quint32 vindex = 0; vindex < _package->variants().size(); ++vindex) {
|
||||||
|
|
|
@ -462,7 +462,7 @@ bool runLauncherDialog()
|
||||||
// we will re-do this later, but we want to access translated strings
|
// we will re-do this later, but we want to access translated strings
|
||||||
// from within the launcher
|
// from within the launcher
|
||||||
globals->get_locale()->selectLanguage(lang);
|
globals->get_locale()->selectLanguage(lang);
|
||||||
globals->packageRoot()->setLocale(lang);
|
globals->packageRoot()->setLocale(globals->get_locale()->getPreferredLanguage());
|
||||||
|
|
||||||
// startup the HTTP system now since packages needs it
|
// startup the HTTP system now since packages needs it
|
||||||
FGHTTPClient* http = globals->add_new_subsystem<FGHTTPClient>();
|
FGHTTPClient* http = globals->add_new_subsystem<FGHTTPClient>();
|
||||||
|
|
Loading…
Reference in a new issue