From: ThorstenB Date: Sat, 21 Apr 2012 18:17:42 +0000 (+0200) Subject: Move locale code to separate module. X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=470552fab1c55a6c8168120997bcd54c115e3bb4;p=flightgear.git Move locale code to separate module. Introduce convenience methods to handle localized string resources and obtain strings. --- diff --git a/src/GUI/gui.cxx b/src/GUI/gui.cxx index f659d325a..851f5dbb3 100644 --- a/src/GUI/gui.cxx +++ b/src/GUI/gui.cxx @@ -41,6 +41,7 @@ #include
#include
+#include
#include
#include
#include @@ -78,8 +79,7 @@ public: FGFontCache *fc = globals->get_fontcache(); fc->initializeFonts(); puFont *GuiFont - = fc->get(globals->get_locale()->getStringValue("font", - "typewriter.txf"), + = fc->get(globals->get_locale()->getDefaultFont("typewriter.txf"), 15); puSetDefaultFonts(*GuiFont, *GuiFont); guiFnt = puGetDefaultLabelFont(); diff --git a/src/GUI/menubar.cxx b/src/GUI/menubar.cxx index 4295f808b..444795fd5 100644 --- a/src/GUI/menubar.cxx +++ b/src/GUI/menubar.cxx @@ -4,9 +4,30 @@ #include "menubar.hxx" +#include
+#include
+ +FGMenuBar::FGMenuBar() +{ + // load locale's menu resources (default and current language) + globals->get_locale()->loadResource("menu"); +} FGMenuBar::~FGMenuBar () { } +const char* +FGMenuBar::getLocalizedLabel(SGPropertyNode* node) +{ + const char* name = node->getStringValue("name", 0); + + const char* translated = globals->get_locale()->getLocalizedString(name, "menu"); + if (translated) + return translated; + + // return default + return node->getStringValue("label"); +} + // end of menubar.cxx diff --git a/src/GUI/menubar.hxx b/src/GUI/menubar.hxx index fa3f43ca5..df3b9cf60 100644 --- a/src/GUI/menubar.hxx +++ b/src/GUI/menubar.hxx @@ -3,6 +3,7 @@ #ifndef __MENUBAR_HXX #define __MENUBAR_HXX 1 +class SGPropertyNode; /** * XML-configured menu bar interface @@ -15,7 +16,7 @@ class FGMenuBar { public: - + FGMenuBar(); /** * Destructor. @@ -45,6 +46,13 @@ public: */ virtual bool isVisible () const = 0; + /** + * Read a menu label from the menu's property tree. + * Take care of mapping it to the appropriate translation, if available. + * Returns an UTF-8 encoded string. + */ + const char* getLocalizedLabel(SGPropertyNode* node); + }; #endif // __MENUBAR_HXX diff --git a/src/Main/CMakeLists.txt b/src/Main/CMakeLists.txt index c155f527e..d136da9cb 100644 --- a/src/Main/CMakeLists.txt +++ b/src/Main/CMakeLists.txt @@ -21,6 +21,7 @@ set(SOURCES fg_props.cxx fgviewer.cxx globals.cxx + locale.cxx logger.cxx main.cxx options.cxx @@ -43,6 +44,7 @@ set(HEADERS fg_props.hxx fgviewer.hxx globals.hxx + locale.hxx logger.hxx main.hxx options.hxx diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 6c255bdc1..21f0624b5 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -149,136 +149,6 @@ string fgBasePackageVersion() { return version; } -// -// Select the proper language based on the given locale/language name. -// -static SGPropertyNode* findLocale(SGPropertyNode *intl, const char* language) -{ - vector localeList = intl->getChildren("locale"); - for (unsigned int i = 0; i < localeList.size(); i++) { - - vector lang = localeList[i]->getChildren("lang"); - for (unsigned int j = 0; j < lang.size(); j++) { - - if (!strcmp(lang[j]->getStringValue(), language)) { - return localeList[i]; - } - } - } - return NULL; -} - -// Initialize the localization -SGPropertyNode *fgInitLocale(const char *language) { - SGPropertyNode *c_node = NULL, *d_node = NULL; - SGPropertyNode *intl = fgGetNode("/sim/intl"); - - SG_LOG(SG_GENERAL, SG_INFO, "Selecting language: " << language ); - - // localization not defined - if (!intl) - return NULL; - - c_node = findLocale(intl, language); - if (!c_node) - { - /* be tolerant about locale names, i.e. instead of "de_DE.utf8" also - * consider "de_DE" ... */ - string l = language; - size_t pos = l.find("."); - if ((pos != string::npos)&&(pos>0)) - c_node = findLocale(intl, l.substr(0, pos).c_str()); - - /* finally consider country alone, i.e. "de" */ - pos = l.find("_"); - if ((!c_node)&&(pos != string::npos)&&(pos>0)) - c_node = findLocale(intl, l.substr(0, pos).c_str()); - } - - - // Get the defaults - d_node = intl->getChild("locale"); - if (!c_node) - c_node = d_node; - - // Check for localized font - SGPropertyNode *font_n = c_node->getNode("font", true); - if ( !strcmp(font_n->getStringValue(), "") ) - font_n->setStringValue(d_node->getStringValue("font", "typewriter.txf")); - - - // - // Load the default strings - // - SGPath d_path( globals->get_fg_root() ); - - const char *d_path_str = d_node->getStringValue("strings"); - if (!d_path_str) { - SG_LOG(SG_GENERAL, SG_ALERT, "No path in " << d_node->getPath() << "/strings."); - return NULL; - } - - d_path.append(d_path_str); - SG_LOG(SG_GENERAL, SG_INFO, "Reading localized strings from " << d_path.str()); - - SGPropertyNode *strings = c_node->getNode("strings"); - try { - readProperties(d_path.str(), strings); - } catch (const sg_exception &) { - SG_LOG(SG_GENERAL, SG_ALERT, "Unable to read the localized strings"); - return NULL; - } - - // - // Load the language specific strings - // - if (c_node != d_node) { - SGPath c_path( globals->get_fg_root() ); - - const char *c_path_str = c_node->getStringValue("strings"); - if (!c_path_str) { - SG_LOG(SG_GENERAL, SG_ALERT, "No path in " << c_node->getPath() << "/strings"); - return NULL; - } - - c_path.append(c_path_str); - SG_LOG(SG_GENERAL, SG_INFO, "Reading localized strings from " << c_path.str()); - - try { - readProperties(c_path.str(), strings); - } catch (const sg_exception &) { - SG_LOG(SG_GENERAL, SG_ALERT, - "Unable to read the localized strings from " << c_path.str()); - return NULL; - } - } - - return c_node; -} - - - -// Initialize the localization routines -bool fgDetectLanguage() { - const char *language = ::getenv("LANG"); - - if (language == NULL) { - SG_LOG(SG_GENERAL, SG_INFO, "Unable to detect the language" ); - language = "C"; - } - - SGPropertyNode *locale = fgInitLocale(language); - if (!locale) { - SG_LOG(SG_GENERAL, SG_ALERT, - "No internationalization settings specified in preferences.xml" ); - - return false; - } - - globals->set_locale( locale ); - - return true; -} template bool fgFindAircraftInDir(const SGPath& dirPath, T* obj, bool (T::*pred)(const SGPath& p)) @@ -561,11 +431,6 @@ bool fgInitConfig ( int argc, char **argv ) fgLoadProps("preferences.xml", globals->get_props()); SG_LOG(SG_INPUT, SG_INFO, "Finished Reading global preferences"); - // Detect the required language as early as possible - if ( !fgDetectLanguage() ) { - return false; - } - globals->loadUserSettings(dataPath); // Scan user config files and command line for a specified aircraft. diff --git a/src/Main/globals.cxx b/src/Main/globals.cxx index 25eb5194b..8adcd57ca 100644 --- a/src/Main/globals.cxx +++ b/src/Main/globals.cxx @@ -59,6 +59,7 @@ #include "globals.hxx" #include "renderer.hxx" #include "viewmgr.hxx" +#include "locale.hxx" #include "fg_props.hxx" #include "fg_io.hxx" @@ -122,7 +123,7 @@ FGGlobals *globals; FGGlobals::FGGlobals() : props( new SGPropertyNode ), initial_state( NULL ), - locale( NULL ), + locale( new FGLocale(props) ), renderer( new FGRenderer ), subsystem_mgr( new SGSubsystemMgr ), event_mgr( new SGEventMgr ), @@ -211,6 +212,9 @@ FGGlobals::~FGGlobals() delete carrierlist; delete channellist; delete sound; + + delete locale; + locale = NULL; } diff --git a/src/Main/globals.hxx b/src/Main/globals.hxx index 9be19ff66..2788455e2 100644 --- a/src/Main/globals.hxx +++ b/src/Main/globals.hxx @@ -61,6 +61,7 @@ class FGControls; class FGFlightPlanDispatcher; class FGNavList; class FGTACANList; +class FGLocale; class FGModelMgr; class FGRouteMgr; class FGScenery; @@ -86,7 +87,7 @@ private: SGPropertyNode_ptr initial_state; // localization - SGPropertyNode_ptr locale; + FGLocale* locale; FGRenderer *renderer; SGSubsystemMgr *subsystem_mgr; @@ -255,8 +256,7 @@ public: inline SGPropertyNode *get_props () { return props; } inline void set_props( SGPropertyNode *n ) { props = n; } - inline SGPropertyNode *get_locale () { return locale; } - inline void set_locale( SGPropertyNode *n ) { locale = n; } + inline FGLocale* get_locale () { return locale; } inline SGCommandMgr *get_commands () { return commands; } diff --git a/src/Main/locale.cxx b/src/Main/locale.cxx new file mode 100644 index 000000000..cd868ca17 --- /dev/null +++ b/src/Main/locale.cxx @@ -0,0 +1,294 @@ +// locale.cxx -- FlightGear Localization Support +// +// Written by Thorsten Brehm, started April 2012. +// +// Copyright (C) 2012 Thorsten Brehm - brehmt (at) gmail com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +#include +#include + +#include "fg_props.hxx" +#include "locale.hxx" + +using std::vector; +using std::string; + +FGLocale::FGLocale(SGPropertyNode* root) : + _intl(root->getNode("/sim/intl",0, true)), + _defaultLocale(_intl->getChild("locale",0, true)) +{ +} + +FGLocale::~FGLocale() +{ +} + +// Search property tree for matching locale description +SGPropertyNode* +FGLocale::findLocaleNode(const string& language) +{ + SG_LOG(SG_GENERAL, SG_INFO, "Searching language resource for locale: " << language); + + SGPropertyNode* node = NULL; + + // remove character encoding from the locale spec, i.e. "de_DE.utf8" => "de_DE" + size_t pos = language.find("."); + if ((pos != string::npos)&&(pos>0)) + { + node = findLocaleNode(language.substr(0, pos)); + if (node) + return node; + } + + // try country's default resource, i.e. "de_DE" => "de" + pos = language.find("_"); + if ((pos != string::npos)&&(pos>0)) + { + node = findLocaleNode(language.substr(0, pos)); + if (node) + return node; + } + + // search locale using full string + vector localeList = _intl->getChildren("locale"); + + for (size_t i = 0; i < localeList.size(); i++) + { + vector langList = localeList[i]->getChildren("lang"); + + for (size_t j = 0; j < langList.size(); j++) + { + if (!language.compare(langList[j]->getStringValue())) + return localeList[i]; + } + } + + return NULL; +} + +// Select the language. When no language is given (NULL), +// a default is determined matching the system locale. +bool +FGLocale::selectLanguage(const char *language) +{ + // Use environment setting when no language is given. + if ((language == NULL)||(language[0]==0)) + language = ::getenv("LANG"); + + // Use plain C locale if nothing is available. + if (language == NULL) + { + SG_LOG(SG_GENERAL, SG_INFO, "Unable to detect the language" ); + language = "C"; + } + + SGPropertyNode *locale = findLocaleNode(language); + if (!locale) + { + SG_LOG(SG_GENERAL, SG_ALERT, + "No internationalization settings specified in preferences.xml" ); + return false; + } + + _currentLocale = locale; + return true; +} + +// Load strings for requested resource and locale. +// Result is stored below "strings" in the property tree of the given locale. +bool +FGLocale::loadResource(SGPropertyNode* localeNode, const char* resource) +{ + SGPath path( globals->get_fg_root() ); + + SGPropertyNode* stringNode = localeNode->getNode("strings", 0, true); + + const char *path_str = stringNode->getStringValue(resource, NULL); + if (!path_str) + { + SG_LOG(SG_GENERAL, SG_ALERT, "No path in " << stringNode->getPath() << "/" << resource << "."); + return NULL; + } + + path.append(path_str); + SG_LOG(SG_GENERAL, SG_INFO, "Reading localized strings for '" << + localeNode->getStringValue("lang", "") + <<"' from " << path.str()); + + // load the actual file + try + { + readProperties(path.str(), stringNode->getNode(resource, 0, true)); + } catch (const sg_exception &e) + { + SG_LOG(SG_GENERAL, SG_ALERT, "Unable to read the localized strings from " << path.str() << + ". Error: " << e.getFormattedMessage()); + return false; + } + + return true; +} + +// Load strings for requested resource (for current and default locale). +// Result is stored below "strings" in the property tree of the locales. +bool +FGLocale::loadResource(const char* resource) +{ + // load defaults first + bool Ok = loadResource(_defaultLocale, resource); + + // also load language specific resource, unless identical + if ((_currentLocale!=0)&& + (_defaultLocale != _currentLocale)) + { + Ok &= loadResource(_currentLocale, resource); + } + + return Ok; +} + +const char* +FGLocale::getLocalizedString(SGPropertyNode *localeNode, const char* id, const char* context) +{ + SGPropertyNode *n = localeNode->getNode("strings",0, true)->getNode(context); + if (n) + return n->getStringValue(id, NULL); + return NULL; +} + +const char* +FGLocale::getLocalizedString(const char* id, const char* resource, const char* Default) +{ + if (id && resource) + { + const char* s = NULL; + if (_currentLocale) + s = getLocalizedString(_currentLocale, id, resource); + if (s && s[0]!=0) + return s; + + if (_defaultLocale) + s = getLocalizedString(_defaultLocale, id, resource); + if (s && s[0]!=0) + return s; + } + return Default; +} + +simgear::PropertyList +FGLocale::getLocalizedStrings(SGPropertyNode *localeNode, const char* id, const char* context) +{ + SGPropertyNode *n = localeNode->getNode("strings",0, true)->getNode(context); + if (n) + { + return n->getChildren(id); + } + return simgear::PropertyList(); +} + +simgear::PropertyList +FGLocale::getLocalizedStrings(const char* id, const char* resource) +{ + if (id && resource) + { + if (_currentLocale) + { + simgear::PropertyList s = getLocalizedStrings(_currentLocale, id, resource); + if (s.size()) + return s; + } + + if (_defaultLocale) + { + simgear::PropertyList s = getLocalizedStrings(_defaultLocale, id, resource); + if (s.size()) + return s; + } + } + return simgear::PropertyList(); +} + +// Check for localized font +const char* +FGLocale::getDefaultFont(const char* fallbackFont) +{ + const char* font = NULL; + if (_currentLocale) + { + font = _currentLocale->getStringValue("font", ""); + if (font[0] != 0) + return font; + } + if (_defaultLocale) + { + font = _defaultLocale->getStringValue("font", ""); + if (font[0] != 0) + return font; + } + + return fallbackFont; +} + +// Simple UTF8 to Latin1 encoder. +void FGLocale::utf8toLatin1(string& s) +{ + size_t pos = 0; + + // map '0xc3..' utf8 characters to Latin1 + while ((string::npos != (pos = s.find('\xc3',pos)))&& + (pos+1 < s.size())) + { + char c='*'; + unsigned char p = s[pos+1]; + if ((p>=0x80)&&(p<0xc0)) + c = 0x40 + p; + char v[2]; + v[0]=c; + v[1]=0; + s.replace(pos, 2, v); + pos++; + } + +#ifdef DEBUG_ENCODING + printf("'%s': ", s.c_str()); + for (pos = 0;pos + +#include + +/////////////////////////////////////////////////////////////////////////////// +// FGLocale ////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class FGLocale +{ +public: + FGLocale(SGPropertyNode* root); + virtual ~FGLocale(); + + /** + * Select the locale's primary language. When no language is given (NULL), + * a default is determined matching the system locale. + */ + bool selectLanguage (const char* language = NULL); + + /** + * Load strings for requested resource, i.e. "menu", "options", "dialogs". + * Loads data for current and default locale (the latter is the fallback). + * Result is stored below the "strings" node in the property tree of the + * respective locale. + */ + bool loadResource (const char* resource); + + /** + * Obtain a single string from the localized resource matching the given identifier. + * Selected context refers to "menu", "options", "dialog" etc. + */ + const char* getLocalizedString (const char* id, const char* resource, const char* Default=NULL); + + /** + * Obtain a list of strings from the localized resource matching the given identifier. + * Selected context refers to "menu", "options", "dialog" etc. + * Returns a list of (string) properties. + */ + simgear::PropertyList getLocalizedStrings(const char* id, const char* resource); + + /** + * Obtain default font for current locale. + */ + const char* getDefaultFont (const char* fallbackFont); + + /** + * Simple UTF8 to Latin1 encoder. + */ + static void utf8toLatin1 (std::string& s); + +protected: + /** + * Find property node matching given language. + */ + SGPropertyNode* findLocaleNode (const std::string& language); + + /** + * Load resource data for given locale node. + */ + bool loadResource (SGPropertyNode* localeNode, const char* resource); + + /** + * Obtain a single string from locale node matching the given identifier and context. + */ + const char* getLocalizedString (SGPropertyNode *localeNode, const char* id, const char* context); + + /** + * Obtain a list of strings from locale node matching the given identifier and context. + */ + simgear::PropertyList getLocalizedStrings(SGPropertyNode *localeNode, const char* id, const char* context); + + SGPropertyNode_ptr _intl; + SGPropertyNode_ptr _currentLocale; + SGPropertyNode_ptr _defaultLocale; +}; + +#endif // __FGLOCALE_HXX diff --git a/src/Main/main.cxx b/src/Main/main.cxx index 5535060d5..dc6043af8 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -499,7 +499,7 @@ int fgMainInit( int argc, char **argv ) { #endif SG_LOG( SG_GENERAL, SG_INFO, "FlightGear: Version " << version ); - SG_LOG( SG_GENERAL, SG_INFO, "Built with " << SG_COMPILER_STR << std::endl ); + SG_LOG( SG_GENERAL, SG_INFO, "Built with " << SG_COMPILER_STR << std::endl ); // Allocate global data structures. This needs to happen before // we parse command line options diff --git a/src/Main/options.cxx b/src/Main/options.cxx index 4ed3f3252..aba0650fe 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -60,6 +60,7 @@ #include "viewmgr.hxx" #include "main.hxx" #include
+#include
#include #include @@ -1971,11 +1972,10 @@ string_list Options::valuesForOption(const std::string& key) const void Options::processOptions() { - // establish locale before showing help - if (isOptionSet("language")) { - globals->set_locale( fgInitLocale( valueForOption("language").c_str() ) ); - } - + // establish locale before showing help (this selects the default locale, + // when no explicit option was set) + globals->get_locale()->selectLanguage(valueForOption("language").c_str()); + // now FG_ROOT is setup, process various command line options that bail us // out quickly, but rely on aircraft / root settings if (p->showHelp) { @@ -2064,12 +2064,12 @@ void Options::showUsage() const { fgOptLogLevel( "alert" ); - SGPropertyNode *locale = globals->get_locale(); + FGLocale *locale = globals->get_locale(); SGPropertyNode options_root; SG_LOG( SG_GENERAL, SG_ALERT, "" ); // To popup the console on Windows cout << endl; - + try { fgLoadProps("options.xml", &options_root); } catch (const sg_exception &) { @@ -2080,17 +2080,23 @@ void Options::showUsage() const exit(-1); } - + SGPropertyNode *options = options_root.getNode("options"); if (!options) { SG_LOG( SG_GENERAL, SG_ALERT, "Error reading options.xml: directive not found." ); exit(-1); } - - SGPropertyNode *usage = locale->getNode(options->getStringValue("usage")); + + if (!locale->loadResource("options")) + { + cout << "Unable to read the language resource." << endl; + exit(-1); + } + + const char* usage = locale->getLocalizedString(options->getStringValue("usage"), "options"); if (usage) { - cout << usage->getStringValue() << endl; + cout << usage << endl; } vectorsection = options->getChildren("section"); @@ -2131,21 +2137,17 @@ void Options::showUsage() const msg += tmp + '\n'; msg.append(32, ' '); } - // There may be more than one tag assosiated + // There may be more than one tag associated // with one option vector desc; desc = option[k]->getChildren("description"); if (desc.size() > 0) { for ( unsigned int l = 0; l < desc.size(); l++) { - - // There may be more than one translation line. - string t = desc[l]->getStringValue(); - SGPropertyNode *n = locale->getNode("strings"); - vectortrans_desc = - n->getChildren(t.substr(8).c_str()); - + + // There may be more than one translation line. + vectortrans_desc = locale->getLocalizedStrings(t.c_str(),"options"); for ( unsigned int m = 0; m < trans_desc.size(); m++ ) { string t_str = trans_desc[m]->getStringValue(); @@ -2171,19 +2173,18 @@ void Options::showUsage() const } } - SGPropertyNode *name; - name = locale->getNode(section[j]->getStringValue("name")); - + const char* name = locale->getLocalizedString(section[j]->getStringValue("name"),"options"); if (!msg.empty() && name) { - cout << endl << name->getStringValue() << ":" << endl; + cout << endl << name << ":" << endl; cout << msg; msg.erase(); } } if ( !p->verbose ) { - cout << endl; - cout << "For a complete list of options use --help --verbose" << endl; + const char* verbose_help = locale->getLocalizedString(options->getStringValue("verbose-help"),"options"); + if (verbose_help) + cout << endl << verbose_help << endl; } #ifdef _MSC_VER std::cout << "Hit a key to continue..." << std::endl;