1 // locale.cxx -- FlightGear Localization Support
3 // Written by Thorsten Brehm, started April 2012.
5 // Copyright (C) 2012 Thorsten Brehm - brehmt (at) gmail com
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
30 #include <boost/foreach.hpp>
32 #include <simgear/props/props_io.hxx>
33 #include <simgear/structure/exception.hxx>
35 #include "fg_props.hxx"
41 FGLocale::FGLocale(SGPropertyNode* root) :
42 _intl(root->getNode("/sim/intl",0, true)),
43 _defaultLocale(_intl->getChild("locale",0, true))
53 * Determine locale/language settings on Windows.
55 * Copyright (C) 1997, 2002, 2003 Martin von Loewis
57 * Permission to use, copy, modify, and distribute this software and its
58 * documentation for any purpose and without fee is hereby granted,
59 * provided that the above copyright notice appear in all copies.
61 * This software comes with no warranty. Use at your own risk.
64 FGLocale::getUserLanguage()
67 static char locale[100] = {0};
69 if (GetLocaleInfo(LOCALE_USER_DEFAULT,
70 LOCALE_SISO639LANGNAME,
71 locale, sizeof(locale)))
73 SG_LOG(SG_GENERAL, SG_DEBUG, "Detected locale's language setting: " << locale);
74 size_t i = strlen(locale);
76 if (GetLocaleInfo(LOCALE_USER_DEFAULT,
77 LOCALE_SISO3166CTRYNAME,
78 locale+i, (int)(sizeof(locale)-i)))
80 result.push_back(locale);
85 SG_LOG(SG_GENERAL, SG_WARN, "Failed to detected locale's country setting.");
86 result.push_back(locale);
93 // implemented in CocoaHelpers.mm
96 * Determine locale/language settings on Linux/Unix.
99 FGLocale::getUserLanguage()
102 const char* langEnv = ::getenv("LANG");
104 result.push_back(langEnv);
111 // Search property tree for matching locale description
113 FGLocale::findLocaleNode(const string& language)
115 SGPropertyNode* node = NULL;
117 // remove character encoding from the locale spec, i.e. "de_DE.utf8" => "de_DE"
118 size_t pos = language.find(".");
119 if ((pos != string::npos)&&(pos>0))
121 node = findLocaleNode(language.substr(0, pos));
126 SG_LOG(SG_GENERAL, SG_DEBUG, "Searching language resource for locale: " << language);
127 // search locale using full string
128 vector<SGPropertyNode_ptr> localeList = _intl->getChildren("locale");
130 for (size_t i = 0; i < localeList.size(); i++)
132 vector<SGPropertyNode_ptr> langList = localeList[i]->getChildren("lang");
134 for (size_t j = 0; j < langList.size(); j++)
136 if (!language.compare(langList[j]->getStringValue()))
138 SG_LOG(SG_GENERAL, SG_INFO, "Found language resource for: " << language);
139 return localeList[i];
144 // try country's default resource, i.e. "de_DE" => "de"
145 pos = language.find("_");
146 if ((pos != string::npos)&&(pos>0))
148 node = findLocaleNode(language.substr(0, pos));
156 // Select the language. When no language is given (NULL),
157 // a default is determined matching the system locale.
159 FGLocale::selectLanguage(const char *language)
161 string_list languages = getUserLanguage();
162 if (languages.empty()) {
163 // Use plain C locale if nothing is available.
164 SG_LOG(SG_GENERAL, SG_WARN, "Unable to detect system language" );
165 languages.push_back("C");
168 // if we were passed a language option, try it first
169 if ((language != NULL) && (strlen(language) > 0)) {
170 languages.insert(languages.begin(), string(language));
174 _currentLocale = NULL;
175 BOOST_FOREACH(string lang, languages) {
176 SG_LOG(SG_GENERAL, SG_DEBUG, "trying to find locale for " << lang );
177 _currentLocale = findLocaleNode(lang);
178 if (_currentLocale) {
179 SG_LOG(SG_GENERAL, SG_DEBUG, "found locale for " << lang << " at " << _currentLocale->getPath() );
184 // load resource for system messages (translations for fgfs internal messages)
187 // load resource for atc messages
192 SG_LOG(SG_GENERAL, SG_ALERT,
193 "System locale not found or no internationalization settings specified in preferences.xml. Using default (en)." );
200 // Load strings for requested resource and locale.
201 // Result is stored below "strings" in the property tree of the given locale.
203 FGLocale::loadResource(SGPropertyNode* localeNode, const char* resource)
205 SGPath path( globals->get_fg_root() );
207 SGPropertyNode* stringNode = localeNode->getNode("strings", 0, true);
209 const char *path_str = stringNode->getStringValue(resource, NULL);
212 SG_LOG(SG_GENERAL, SG_WARN, "No path in " << stringNode->getPath() << "/" << resource << ".");
216 path.append(path_str);
217 SG_LOG(SG_GENERAL, SG_INFO, "Reading localized strings for '" <<
218 localeNode->getStringValue("lang", "<none>")
219 <<"' from " << path);
221 // load the actual file
224 readProperties(path, stringNode->getNode(resource, 0, true));
225 } catch (const sg_exception &e)
227 SG_LOG(SG_GENERAL, SG_ALERT, "Unable to read the localized strings from " << path <<
228 ". Error: " << e.getFormattedMessage());
235 // Load strings for requested resource (for current and default locale).
236 // Result is stored below "strings" in the property tree of the locales.
238 FGLocale::loadResource(const char* resource)
240 // load defaults first
241 bool Ok = loadResource(_defaultLocale, resource);
243 // also load language specific resource, unless identical
244 if ((_currentLocale!=0)&&
245 (_defaultLocale != _currentLocale))
247 Ok &= loadResource(_currentLocale, resource);
254 FGLocale::getLocalizedString(SGPropertyNode *localeNode, const char* id, const char* context)
256 SGPropertyNode *n = localeNode->getNode("strings",0, true)->getNode(context);
258 return n->getStringValue(id, NULL);
263 FGLocale::getLocalizedString(const char* id, const char* resource, const char* Default)
267 const char* s = NULL;
269 s = getLocalizedString(_currentLocale, id, resource);
274 s = getLocalizedString(_defaultLocale, id, resource);
281 simgear::PropertyList
282 FGLocale::getLocalizedStrings(SGPropertyNode *localeNode, const char* id, const char* context)
284 SGPropertyNode *n = localeNode->getNode("strings",0, true)->getNode(context);
287 return n->getChildren(id);
289 return simgear::PropertyList();
292 simgear::PropertyList
293 FGLocale::getLocalizedStrings(const char* id, const char* resource)
299 simgear::PropertyList s = getLocalizedStrings(_currentLocale, id, resource);
306 simgear::PropertyList s = getLocalizedStrings(_defaultLocale, id, resource);
311 return simgear::PropertyList();
314 // Check for localized font
316 FGLocale::getDefaultFont(const char* fallbackFont)
318 const char* font = NULL;
321 font = _currentLocale->getStringValue("font", "");
327 font = _defaultLocale->getStringValue("font", "");
335 std::string FGLocale::localizedPrintf(const char* id, const char* resource, ... )
338 va_start(args, resource);
339 string r = vlocalizedPrintf(id, resource, args);
344 std::string FGLocale::vlocalizedPrintf(const char* id, const char* resource, va_list args)
346 const char* format = getLocalizedString(id, resource);
347 int len = ::vsprintf(NULL, format, args);
348 char* buf = (char*) alloca(len);
349 ::vsprintf(buf, format, args);
350 return std::string(buf);
353 // Simple UTF8 to Latin1 encoder.
354 void FGLocale::utf8toLatin1(string& s)
358 // map '0xc3..' utf8 characters to Latin1
359 while ((string::npos != (pos = s.find('\xc3',pos)))&&
363 unsigned char p = s[pos+1];
364 if ((p>=0x80)&&(p<0xc0))
369 s.replace(pos, 2, v);
373 #ifdef DEBUG_ENCODING
374 printf("'%s': ", s.c_str());
375 for (pos = 0;pos<s.size();pos++)
376 printf("%02x ", (unsigned char) s[pos]);
380 // hack: also map some Latin2 characters to plain-text ASCII
382 while ((string::npos != (pos = s.find('\xc5',pos)))&&
386 unsigned char p = s[pos+1];
389 case 0x82:c='l';break;
390 case 0x9a:c='S';break;
391 case 0x9b:c='s';break;
392 case 0xba:c='z';break;
393 case 0xbc:c='z';break;
398 s.replace(pos, 2, v);
403 const char* fgTrMsg(const char* key)
405 return globals->get_locale()->getLocalizedString(key, "message");
408 std::string fgTrPrintfMsg(const char* key, ...)
412 string r = globals->get_locale()->vlocalizedPrintf(key, "message", args);