1
0
Fork 0

Add-ons localization support

Add-on meta-data can be localized now, in the same manner as aircraft
metadata.
This commit is contained in:
James Turner 2020-07-09 18:14:56 +01:00
parent f0d3663102
commit 0bb9600795
6 changed files with 102 additions and 29 deletions

View file

@ -469,6 +469,14 @@ Addon::readMenubarItems(const SGPath& menuFile)
return res;
}
void Addon::retranslate()
{
Addon::Metadata metadata = MetadataParser::parseMetadataFile(_basePath);
setName(std::move(metadata.name));
setShortDescription(std::move(metadata.shortDescription));
setLongDescription(std::move(metadata.longDescription));
}
// Static method
void Addon::setupGhost(nasal::Hash& addonsModule)
{

View file

@ -195,7 +195,12 @@ public:
static void setupGhost(nasal::Hash& addonsModule);
private:
/**
* @brief update string values (description, etc) based on the active locale
*/
void retranslate();
private:
class Metadata;
class MetadataParser;

View file

@ -35,6 +35,9 @@
#include "exceptions.hxx"
#include "pointer_traits.hxx"
#include <Main/globals.hxx>
#include <Main/locale.hxx>
namespace strutils = simgear::strutils;
using std::string;
@ -53,6 +56,23 @@ Addon::MetadataParser::getMetadataFile(const SGPath& addonPath)
return addonPath / "addon-metadata.xml";
}
static string getMaybeLocalized(const string& tag, SGPropertyNode* base, SGPropertyNode* lang)
{
if (lang) {
auto n = lang->getChild(tag);
if (n) {
return strutils::strip(n->getStringValue());
}
}
auto n = base->getChild(tag);
if (n) {
return strutils::strip(n->getStringValue());
}
return {};
}
// Static method
Addon::Metadata
Addon::MetadataParser::parseMetadataFile(const SGPath& addonPath)
@ -121,6 +141,9 @@ Addon::MetadataParser::parseMetadataFile(const SGPath& addonPath)
metadataFile.utf8Str() + "'");
}
SGPropertyNode* localizedNode = addonNode->getChild("localized");
SGPropertyNode* langStringsNode = globals->get_locale()->selectLanguageNode(localizedNode);
SGPropertyNode *idNode = addonNode->getChild("identifier");
if (idNode == nullptr) {
throw errors::error_loading_metadata_file(
@ -147,7 +170,8 @@ Addon::MetadataParser::parseMetadataFile(const SGPath& addonPath)
"no /addon/name node found in add-on metadata file '" +
metadataFile.utf8Str() + "'");
}
metadata.name = strutils::strip(nameNode->getStringValue());
metadata.name = getMaybeLocalized("name", addonNode, langStringsNode);
// Require a non-empty name for the add-on
if (metadata.name.empty()) {
@ -170,19 +194,8 @@ Addon::MetadataParser::parseMetadataFile(const SGPath& addonPath)
metadata.maintainers = parseContactsNode<Maintainer>(
metadataFile, addonNode->getChild("maintainers"));
SGPropertyNode *shortDescNode = addonNode->getChild("short-description");
if (shortDescNode != nullptr) {
metadata.shortDescription = strutils::strip(shortDescNode->getStringValue());
} else {
metadata.shortDescription = string();
}
SGPropertyNode *longDescNode = addonNode->getChild("long-description");
if (longDescNode != nullptr) {
metadata.longDescription = strutils::strip(longDescNode->getStringValue());
} else {
metadata.longDescription = string();
}
metadata.shortDescription = getMaybeLocalized("short-description", addonNode, langStringsNode);
metadata.longDescription = getMaybeLocalized("long-description", addonNode, langStringsNode);
std::tie(metadata.licenseDesignation, metadata.licenseFile,
metadata.licenseUrl) = parseLicenseNode(addonPath, addonNode);

View file

@ -194,7 +194,7 @@ QWindow* qtWindowFromOSG(osgViewer::GraphicsWindow* graphicsWindow)
} // of namespace flightgear
string_list FGLocale::getUserLanguage()
string_list FGLocale::getUserLanguages()
{
CocoaAutoreleasePool ap;
string_list result;

View file

@ -37,6 +37,8 @@
#include "locale.hxx"
#include "XLIFFParser.hxx"
#include <Add-ons/AddonMetadataParser.hxx>
using std::vector;
using std::string;
@ -67,11 +69,24 @@ string FGLocale::removeEncodingPart(const string& locale)
return res;
}
string removeLocalePart(const string& locale)
{
std::size_t pos = locale.find('_');
if (pos == string::npos) {
pos = locale.find('-');
}
if (pos == string::npos)
return {};
return locale.substr(0, pos);
}
#ifdef _WIN32
string_list
FGLocale::getUserLanguage()
FGLocale::getUserLanguages()
{
unsigned long bufSize = 128;
wchar_t* localeNameBuf = reinterpret_cast<wchar_t*>(alloca(bufSize));
@ -119,7 +134,7 @@ FGLocale::getUserLanguage()
* Determine locale/language settings on Linux/Unix.
*/
string_list
FGLocale::getUserLanguage()
FGLocale::getUserLanguages()
{
string_list result;
const char* langEnv = ::getenv("LANG");
@ -171,9 +186,9 @@ FGLocale::findLocaleNode(const string& localeSpec)
pos = language.find('-');
}
if ((pos != string::npos) && (pos > 0))
{
node = findLocaleNode(language.substr(0, pos));
const auto justTheLanguage = removeLocalePart(language);
if (!justTheLanguage.empty()) {
node = findLocaleNode(justTheLanguage);
if (node)
return node;
}
@ -185,26 +200,26 @@ FGLocale::findLocaleNode(const string& localeSpec)
// a default is determined matching the system locale.
bool FGLocale::selectLanguage(const std::string& language)
{
string_list languages = getUserLanguage();
if (languages.empty()) {
_languages = getUserLanguages();
if (_languages.empty()) {
// Use plain C locale if nothing is available.
SG_LOG(SG_GENERAL, SG_WARN, "Unable to detect system language" );
languages.push_back("C");
_languages.push_back("C");
}
// if we were passed a language option, try it first
if (!language.empty()) {
languages.insert(languages.begin(), language);
_languages.insert(_languages.begin(), language);
}
_currentLocaleString = removeEncodingPart(languages[0]);
_currentLocaleString = removeEncodingPart(_languages.front());
if (_currentLocaleString == "C") {
_currentLocaleString.clear();
}
_currentLocale = nullptr;
for (const string& lang: languages) {
for (const string& lang : _languages) {
SG_LOG(SG_GENERAL, SG_DEBUG,
"Trying to find locale for '" << lang << "'");
_currentLocale = findLocaleNode(lang);
@ -216,7 +231,7 @@ bool FGLocale::selectLanguage(const std::string& language)
break;
}
}
if (_currentLocale && _currentLocale->hasChild("xliff")) {
parseXLIFF(_currentLocale);
}
@ -242,6 +257,7 @@ void FGLocale::clear()
{
_inited = false;
_currentLocaleString.clear();
_languages.clear();
if (_currentLocale && (_currentLocale != _defaultLocale)) {
// remove loaded strings, so we don't duplicate
@ -585,3 +601,23 @@ std::string fgTrPrintfMsg(const char* key, ...)
va_end(args);
return r;
}
SGPropertyNode_ptr FGLocale::selectLanguageNode(SGPropertyNode* langs) const
{
if (!langs)
return {};
for (auto l : _languages) {
const auto langNoEncoding = removeEncodingPart(l);
if (langs->hasChild(langNoEncoding)) {
return langs->getChild(langNoEncoding);
}
const auto justLang = removeLocalePart(langNoEncoding);
if (langs->hasChild(justLang)) {
return langs->getChild(justLang);
}
}
return {};
}

View file

@ -118,6 +118,12 @@ public:
*/
void clear();
/**
@ brief given a node with children corresponding to different language / locale codes,
select one based on the user preferred langauge
*/
SGPropertyNode_ptr selectLanguageNode(SGPropertyNode* langs) const;
protected:
/**
* Find property node matching given language.
@ -142,7 +148,7 @@ protected:
/**
* Obtain user's default language setting.
*/
string_list getUserLanguage();
string_list getUserLanguages();
SGPropertyNode_ptr _intl;
SGPropertyNode_ptr _currentLocale;
@ -161,6 +167,11 @@ private:
*/
static std::string removeEncodingPart(const std::string& locale);
// this is the ordered list of languages to try. It's the same as
// returned by getUserLanguages except if the user has used --langauge to
// override, that will be the first item.
string_list _languages;
bool _inited = false;
};