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) {
|
||||
int variantIndex = role - AircraftVariantDescriptionRole;
|
||||
if (variantIndex == 0) {
|
||||
return item->description;
|
||||
return item->name();
|
||||
}
|
||||
|
||||
Q_ASSERT(variantIndex < item->variants.size());
|
||||
return item->variants.at(variantIndex)->description;
|
||||
return item->variants.at(variantIndex)->name();
|
||||
}
|
||||
|
||||
if (state.variant) {
|
||||
|
@ -292,11 +292,11 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta
|
|||
}
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
if (item->description.isEmpty()) {
|
||||
if (item->name().isEmpty()) {
|
||||
return tr("Missing description for: %1").arg(item->baseName());
|
||||
}
|
||||
|
||||
return item->description;
|
||||
return item->name();
|
||||
} else if (role == AircraftPathRole) {
|
||||
return item->path;
|
||||
} else if (role == AircraftAuthorsRole) {
|
||||
|
@ -318,7 +318,7 @@ QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateSta
|
|||
}
|
||||
return have;
|
||||
} else if (role == AircraftLongDescriptionRole) {
|
||||
return item->longDescription;
|
||||
return item->description();
|
||||
} else if (role == AircraftIsHelicopterRole) {
|
||||
return item->usesHeliports;
|
||||
} else if (role == AircraftIsSeaplaneRole) {
|
||||
|
@ -564,14 +564,14 @@ QString AircraftItemModel::nameForAircraftURI(QUrl uri) const
|
|||
|
||||
const QString path = uri.toLocalFile();
|
||||
if (item->path == path) {
|
||||
return item->description;
|
||||
return item->name();
|
||||
}
|
||||
|
||||
// check variants too
|
||||
for (int vr=0; vr < item->variants.size(); ++vr) {
|
||||
auto variant = item->variants.at(vr);
|
||||
if (variant->path == path) {
|
||||
return variant->description;
|
||||
return variant->name();
|
||||
}
|
||||
}
|
||||
} else if (uri.scheme() == "package") {
|
||||
|
|
|
@ -31,17 +31,28 @@
|
|||
#include <QSettings>
|
||||
#include <QDebug>
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
#include <Include/version.h>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/locale.hxx>
|
||||
|
||||
#include <simgear/misc/ResourceManager.hxx>
|
||||
#include <simgear/props/props_io.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)
|
||||
|
@ -62,10 +73,9 @@ AircraftItem::AircraftItem(QDir dir, QString filePath)
|
|||
return;
|
||||
}
|
||||
|
||||
description = sim->getStringValue("description");
|
||||
// trim the description to ensure the alphabetical sort
|
||||
// doesn't get thrown off by leading whitespace
|
||||
description = description.trimmed();
|
||||
LocalizedStrings ls;
|
||||
ls.locale = "en";
|
||||
ls.strings["name"] = QString::fromStdString(sim->getStringValue("description")).trimmed();
|
||||
authors = sim->getStringValue("author");
|
||||
|
||||
if (sim->hasChild("rating")) {
|
||||
|
@ -77,7 +87,7 @@ AircraftItem::AircraftItem(QDir dir, QString filePath)
|
|||
|
||||
if (sim->hasChild("long-description")) {
|
||||
// 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")) {
|
||||
|
@ -132,6 +142,65 @@ AircraftItem::AircraftItem(QDir dir, QString filePath)
|
|||
homepageUrl = QUrl(QString::fromStdString(sim->getStringValue("urls/home-page")));
|
||||
supportUrl = QUrl(QString::fromStdString(sim->getStringValue("urls/support")));
|
||||
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
|
||||
|
@ -148,7 +217,7 @@ void AircraftItem::fromDataStream(QDataStream& ds)
|
|||
return;
|
||||
}
|
||||
|
||||
ds >> description >> longDescription >> authors >> variantOf >> isPrimary;
|
||||
ds >> authors >> variantOf >> isPrimary;
|
||||
for (int i=0; i<4; ++i) ds >> ratings[i];
|
||||
ds >> previews;
|
||||
ds >> thumbnailPath;
|
||||
|
@ -156,6 +225,9 @@ void AircraftItem::fromDataStream(QDataStream& ds)
|
|||
ds >> needsMaintenance >> usesHeliports >> usesSeaports;
|
||||
ds >> homepageUrl >> supportUrl >> wikipediaUrl;
|
||||
ds >> tags;
|
||||
ds >> _localized;
|
||||
|
||||
doLocalizeStrings();
|
||||
}
|
||||
|
||||
void AircraftItem::toDataStream(QDataStream& ds) const
|
||||
|
@ -165,7 +237,7 @@ void AircraftItem::toDataStream(QDataStream& ds) const
|
|||
return;
|
||||
}
|
||||
|
||||
ds << description << longDescription << authors << variantOf << isPrimary;
|
||||
ds << authors << variantOf << isPrimary;
|
||||
for (int i=0; i<4; ++i) ds << ratings[i];
|
||||
ds << previews;
|
||||
ds << thumbnailPath;
|
||||
|
@ -173,6 +245,7 @@ void AircraftItem::toDataStream(QDataStream& ds) const
|
|||
ds << needsMaintenance << usesHeliports << usesSeaports;
|
||||
ds << homepageUrl << supportUrl << wikipediaUrl;
|
||||
ds << tags;
|
||||
ds << _localized;
|
||||
}
|
||||
|
||||
int AircraftItem::indexOfVariant(QUrl uri) const
|
||||
|
@ -216,6 +289,7 @@ public:
|
|||
|
||||
SGPath resolve(const std::string& aResource, SGPath& aContext) const override
|
||||
{
|
||||
Q_UNUSED(aContext)
|
||||
string_list pieces(sgPathBranchSplit(aResource));
|
||||
if ((pieces.size() < 3) || (pieces.front() != "Aircraft")) {
|
||||
return SGPath{}; // not an Aircraft path
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include <QDir>
|
||||
#include <QVariant>
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
class QDataStream;
|
||||
struct AircraftItem;
|
||||
class AircraftScanThread;
|
||||
|
@ -38,13 +40,17 @@ typedef QSharedPointer<AircraftItem> AircraftItemPtr;
|
|||
|
||||
struct AircraftItem
|
||||
{
|
||||
AircraftItem();
|
||||
AircraftItem() = default;
|
||||
|
||||
AircraftItem(QDir dir, QString filePath);
|
||||
|
||||
// the file-name without -set.xml suffix
|
||||
QString baseName() const;
|
||||
|
||||
QString name() const;
|
||||
|
||||
QString description() const;
|
||||
|
||||
void fromDataStream(QDataStream& ds);
|
||||
|
||||
void toDataStream(QDataStream& ds) const;
|
||||
|
@ -53,8 +59,7 @@ struct AircraftItem
|
|||
|
||||
bool excluded = false;
|
||||
QString path;
|
||||
QString description;
|
||||
QString longDescription;
|
||||
|
||||
QString authors; // legacy authors data only
|
||||
int ratings[4] = {0, 0, 0, 0};
|
||||
QString variantOf;
|
||||
|
@ -72,7 +77,31 @@ struct AircraftItem
|
|||
QUrl wikipediaUrl;
|
||||
QUrl supportUrl;
|
||||
QVariant status(int variant);
|
||||
|
||||
|
||||
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
|
||||
|
|
|
@ -342,7 +342,7 @@ quint32 QmlAircraftInfo::numVariants() const
|
|||
QString QmlAircraftInfo::name() const
|
||||
{
|
||||
if (_item) {
|
||||
return resolveItem()->description;
|
||||
return resolveItem()->name();
|
||||
} else if (_package) {
|
||||
return QString::fromStdString(_package->nameForVariant(_variant));
|
||||
}
|
||||
|
@ -353,7 +353,7 @@ QString QmlAircraftInfo::name() const
|
|||
QString QmlAircraftInfo::description() const
|
||||
{
|
||||
if (_item) {
|
||||
return resolveItem()->longDescription;
|
||||
return resolveItem()->description();
|
||||
} else if (_package) {
|
||||
std::string longDesc = _package->getLocalisedProp("description", _variant);
|
||||
return QString::fromStdString(longDesc).simplified();
|
||||
|
@ -773,12 +773,12 @@ QStringList QmlAircraftInfo::variantNames() const
|
|||
{
|
||||
QStringList result;
|
||||
if (_item) {
|
||||
result.append(_item->description);
|
||||
result.append(_item->name());
|
||||
Q_FOREACH(auto v, _item->variants) {
|
||||
if (v->description.isEmpty()) {
|
||||
if (v->name().isEmpty()) {
|
||||
qWarning() << Q_FUNC_INFO << "missing description for " << v->path;
|
||||
}
|
||||
result.append(v->description);
|
||||
result.append(v->name());
|
||||
}
|
||||
} else if (_package) {
|
||||
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
|
||||
// from within the launcher
|
||||
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
|
||||
FGHTTPClient* http = globals->add_new_subsystem<FGHTTPClient>();
|
||||
|
|
Loading…
Reference in a new issue