Add-ons localization support
Add-on meta-data can be localized now, in the same manner as aircraft metadata.
This commit is contained in:
parent
f0d3663102
commit
0bb9600795
6 changed files with 102 additions and 29 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue