1
0
Fork 0
flightgear/src/Main/locale.cxx

400 lines
10 KiB
C++
Raw Normal View History

// 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#include <boost/foreach.hpp>
#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()
{
}
2012-05-08 21:04:53 +00:00
#ifdef _WIN32
/**
* Determine locale/language settings on Windows.
*
* Copyright (C) 1997, 2002, 2003 Martin von Loewis
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted,
* provided that the above copyright notice appear in all copies.
*
* This software comes with no warranty. Use at your own risk.
*/
string_list
FGLocale::getUserLanguage()
{
string_list result;
static char locale[100] = {0};
if (GetLocaleInfo(LOCALE_USER_DEFAULT,
LOCALE_SISO639LANGNAME,
locale, sizeof(locale)))
{
SG_LOG(SG_GENERAL, SG_DEBUG, "Detected locale's language setting: " << locale);
size_t i = strlen(locale);
locale[i++] = '_';
if (GetLocaleInfo(LOCALE_USER_DEFAULT,
LOCALE_SISO3166CTRYNAME,
locale+i, (int)(sizeof(locale)-i)))
{
result.push_back(locale);
return result;
}
2012-05-08 21:04:53 +00:00
locale[--i] = 0;
SG_LOG(SG_GENERAL, SG_WARN, "Failed to detected locale's country setting.");
result.push_back(locale);
return result;
}
return result;
}
#elif __APPLE__
// determine locale / langauge on Mac
#include <CoreFoundation/CoreFoundation.h>
string_list
FGLocale::getUserLanguage()
{
string_list result;
CFArrayRef langs = CFLocaleCopyPreferredLanguages();
char buffer[64];
for (int i=0; i<CFArrayGetCount(langs); ++i) {
CFStringRef s = (CFStringRef) CFArrayGetValueAtIndex(langs, i);
CFStringGetCString(s, buffer, 64, kCFStringEncodingASCII);
result.push_back(buffer);
}
CFRelease(langs);
return result;
}
#else
/**
* Determine locale/language settings on Linux/Unix.
*/
string_list
FGLocale::getUserLanguage()
{
string_list result;
const char* langEnv = ::getenv("LANG");
if (langEnv) {
result.push_back(langEnv);
}
return result;
}
#endif
// Search property tree for matching locale description
SGPropertyNode*
FGLocale::findLocaleNode(const string& 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;
}
SG_LOG(SG_GENERAL, SG_DEBUG, "Searching language resource for locale: " << language);
// 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++)
{
2012-05-08 21:04:53 +00:00
if (!language.compare(langList[j]->getStringValue()))
{
SG_LOG(SG_GENERAL, SG_INFO, "Found language resource for: " << language);
return localeList[i];
}
}
}
2012-05-08 21:04:53 +00:00
// 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;
}
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)
{
string_list languages = getUserLanguage();
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");
}
// if we were passed a language option, try it first
if ((language != NULL) && (strlen(language) > 0)) {
languages.insert(languages.begin(), string(language));
}
SGPropertyNode *locale = NULL;
BOOST_FOREACH(string lang, languages) {
locale = findLocaleNode(lang);
if (locale) {
break;
}
}
if (!locale)
{
SG_LOG(SG_GENERAL, SG_ALERT,
"No internationalization settings specified in preferences.xml" );
return false;
}
_currentLocale = locale;
// load resource for system messages (translations for fgfs internal messages)
loadResource("sys");
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_WARN, "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.empty())
return s;
}
if (_defaultLocale)
{
simgear::PropertyList s = getLocalizedStrings(_defaultLocale, id, resource);
if (! s.empty())
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++;
}
}