#include <Main/main.hxx>
#include <Main/globals.hxx>
+#include <Main/locale.hxx>
#include <Main/fg_props.hxx>
#include <Main/WindowSystemAdapter.hxx>
#include <GUI/new_gui.hxx>
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();
#include "menubar.hxx"
+#include <Main/locale.hxx>
+#include <Main/globals.hxx>
+
+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
#ifndef __MENUBAR_HXX
#define __MENUBAR_HXX 1
+class SGPropertyNode;
/**
* XML-configured menu bar interface
class FGMenuBar
{
public:
-
+ FGMenuBar();
/**
* Destructor.
*/
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
fg_props.cxx
fgviewer.cxx
globals.cxx
+ locale.cxx
logger.cxx
main.cxx
options.cxx
fg_props.hxx
fgviewer.hxx
globals.hxx
+ locale.hxx
logger.hxx
main.hxx
options.hxx
return version;
}
-//
-// Select the proper language based on the given locale/language name.
-//
-static SGPropertyNode* findLocale(SGPropertyNode *intl, const char* language)
-{
- vector<SGPropertyNode_ptr> localeList = intl->getChildren("locale");
- for (unsigned int i = 0; i < localeList.size(); i++) {
-
- vector<SGPropertyNode_ptr> 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 <class T>
bool fgFindAircraftInDir(const SGPath& dirPath, T* obj, bool (T::*pred)(const SGPath& p))
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.
#include "globals.hxx"
#include "renderer.hxx"
#include "viewmgr.hxx"
+#include "locale.hxx"
#include "fg_props.hxx"
#include "fg_io.hxx"
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 ),
delete carrierlist;
delete channellist;
delete sound;
+
+ delete locale;
+ locale = NULL;
}
class FGFlightPlanDispatcher;
class FGNavList;
class FGTACANList;
+class FGLocale;
class FGModelMgr;
class FGRouteMgr;
class FGScenery;
SGPropertyNode_ptr initial_state;
// localization
- SGPropertyNode_ptr locale;
+ FGLocale* locale;
FGRenderer *renderer;
SGSubsystemMgr *subsystem_mgr;
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; }
--- /dev/null
+// 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 <simgear/props/props_io.hxx>
+#include <simgear/structure/exception.hxx>
+
+#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<SGPropertyNode_ptr> localeList = _intl->getChildren("locale");
+
+ for (size_t i = 0; i < localeList.size(); i++)
+ {
+ vector<SGPropertyNode_ptr> 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", "<none>")
+ <<"' 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<s.size();pos++)
+ printf("%02x ", (unsigned char) s[pos]);
+ printf("\n");
+#endif
+
+ // hack: also map some Latin2 characters to plain-text ASCII
+ pos = 0;
+ while ((string::npos != (pos = s.find('\xc5',pos)))&&
+ (pos+1 < s.size()))
+ {
+ char c='*';
+ unsigned char p = s[pos+1];
+ switch(p)
+ {
+ case 0x82:c='l';break;
+ case 0x9a:c='S';break;
+ case 0x9b:c='s';break;
+ case 0xba:c='z';break;
+ case 0xbc:c='z';break;
+ }
+ char v[2];
+ v[0]=c;
+ v[1]=0;
+ s.replace(pos, 2, v);
+ pos++;
+ }
+}
--- /dev/null
+// locale.hxx -- 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.
+
+#ifndef __FGLOCALE_HXX
+#define __FGLOCALE_HXX
+
+#include <string>
+
+#include <simgear/props/props.hxx>
+
+///////////////////////////////////////////////////////////////////////////////
+// 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
#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
#include "viewmgr.hxx"
#include "main.hxx"
#include <Main/viewer.hxx>
+#include <Main/locale.hxx>
#include <Environment/presets.hxx>
#include <osg/Version>
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) {
{
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 &) {
exit(-1);
}
-
+
SGPropertyNode *options = options_root.getNode("options");
if (!options) {
SG_LOG( SG_GENERAL, SG_ALERT,
"Error reading options.xml: <options> 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;
}
vector<SGPropertyNode_ptr>section = options->getChildren("section");
msg += tmp + '\n';
msg.append(32, ' ');
}
- // There may be more than one <description> tag assosiated
+ // There may be more than one <description> tag associated
// with one option
vector<SGPropertyNode_ptr> 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");
- vector<SGPropertyNode_ptr>trans_desc =
- n->getChildren(t.substr(8).c_str());
-
+
+ // There may be more than one translation line.
+ vector<SGPropertyNode_ptr>trans_desc = locale->getLocalizedStrings(t.c_str(),"options");
for ( unsigned int m = 0; m < trans_desc.size(); m++ ) {
string t_str = trans_desc[m]->getStringValue();
}
}
- 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;