]> git.mxchange.org Git - flightgear.git/blob - src/Main/locale.cxx
Fix some inverted logic
[flightgear.git] / src / Main / locale.cxx
1 // locale.cxx -- FlightGear Localization Support
2 //
3 // Written by Thorsten Brehm, started April 2012.
4 //
5 // Copyright (C) 2012 Thorsten Brehm - brehmt (at) gmail com
6 //
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.
11 //
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.
16 //
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.
20
21 #ifdef HAVE_CONFIG_H
22 #  include <config.h>
23 #endif
24
25 #ifdef HAVE_WINDOWS_H
26 #include <windows.h>
27 #endif
28
29 #include <cstdio>
30 #include <boost/foreach.hpp>
31
32 #include <simgear/props/props_io.hxx>
33 #include <simgear/structure/exception.hxx>
34
35 #include "fg_props.hxx"
36 #include "locale.hxx"
37
38 using std::vector;
39 using std::string;
40
41 FGLocale::FGLocale(SGPropertyNode* root) :
42     _intl(root->getNode("/sim/intl",0, true)),
43     _defaultLocale(_intl->getChild("locale",0, true))
44 {
45 }
46
47 FGLocale::~FGLocale()
48 {
49 }
50
51 #ifdef _WIN32
52 /**
53  * Determine locale/language settings on Windows.
54  *
55  * Copyright (C) 1997, 2002, 2003 Martin von Loewis
56  *
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.
60  *
61  * This software comes with no warranty. Use at your own risk.
62  */
63 string_list
64 FGLocale::getUserLanguage()
65 {
66     string_list result;
67     static char locale[100] = {0};
68
69     if (GetLocaleInfo(LOCALE_USER_DEFAULT,
70                       LOCALE_SISO639LANGNAME,
71                       locale, sizeof(locale)))
72     {
73         SG_LOG(SG_GENERAL, SG_DEBUG, "Detected locale's language setting: " << locale);
74         size_t i = strlen(locale);
75         locale[i++] = '_';
76         if (GetLocaleInfo(LOCALE_USER_DEFAULT,
77                           LOCALE_SISO3166CTRYNAME,
78                           locale+i, (int)(sizeof(locale)-i)))
79         {
80             result.push_back(locale);
81             return result;
82         }
83         
84         locale[--i] = 0;
85         SG_LOG(SG_GENERAL, SG_WARN, "Failed to detected locale's country setting.");
86         result.push_back(locale);
87         return result;
88     }
89
90     return result;
91 }
92 #elif __APPLE__
93 //  implemented in CocoaHelpers.mm
94 #else
95 /**
96  * Determine locale/language settings on Linux/Unix.
97  */
98 string_list
99 FGLocale::getUserLanguage()
100 {
101     string_list result;
102     const char* langEnv = ::getenv("LANG");
103     if (langEnv) {
104         result.push_back(langEnv);
105     }
106     
107     return result;
108 }
109 #endif
110
111 // Search property tree for matching locale description
112 SGPropertyNode*
113 FGLocale::findLocaleNode(const string& language)
114 {
115     SGPropertyNode* node = NULL;
116
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))
120     {
121         node = findLocaleNode(language.substr(0, pos));
122         if (node)
123             return node;
124     }
125
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");
129
130     for (size_t i = 0; i < localeList.size(); i++)
131     {
132        vector<SGPropertyNode_ptr> langList = localeList[i]->getChildren("lang");
133
134        for (size_t j = 0; j < langList.size(); j++)
135        {
136            if (!language.compare(langList[j]->getStringValue()))
137            {
138                SG_LOG(SG_GENERAL, SG_INFO, "Found language resource for: " << language);
139                return localeList[i];
140            }
141        }
142     }
143
144     // try country's default resource, i.e. "de_DE" => "de"
145     pos = language.find("_");
146     if ((pos != string::npos)&&(pos>0))
147     {
148         node = findLocaleNode(language.substr(0, pos));
149         if (node)
150             return node;
151     }
152
153     return NULL;
154 }
155
156 // Select the language. When no language is given (NULL),
157 // a default is determined matching the system locale.
158 bool
159 FGLocale::selectLanguage(const char *language)
160 {
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");
166     }
167     
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));
171     }
172
173     
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() );
180             break;
181         }
182     }
183     
184     // load resource for system messages (translations for fgfs internal messages)
185     loadResource("sys");
186
187     // load resource for atc messages
188     loadResource("atc");
189
190     if (!_currentLocale)
191     {
192        SG_LOG(SG_GENERAL, SG_ALERT,
193               "System locale not found or no internationalization settings specified in preferences.xml. Using default (en)." );
194        return false;
195     }
196
197     return true;
198 }
199
200 // Load strings for requested resource and locale.
201 // Result is stored below "strings" in the property tree of the given locale.
202 bool
203 FGLocale::loadResource(SGPropertyNode* localeNode, const char* resource)
204 {
205     SGPath path( globals->get_fg_root() );
206
207     SGPropertyNode* stringNode = localeNode->getNode("strings", 0, true);
208
209     const char *path_str = stringNode->getStringValue(resource, NULL);
210     if (!path_str)
211     {
212         SG_LOG(SG_GENERAL, SG_WARN, "No path in " << stringNode->getPath() << "/" << resource << ".");
213         return false;
214     }
215
216     path.append(path_str);
217     SG_LOG(SG_GENERAL, SG_INFO, "Reading localized strings for '" <<
218            localeNode->getStringValue("lang", "<none>")
219            <<"' from " << path.str());
220
221     // load the actual file
222     try
223     {
224         readProperties(path.str(), stringNode->getNode(resource, 0, true));
225     } catch (const sg_exception &e)
226     {
227         SG_LOG(SG_GENERAL, SG_ALERT, "Unable to read the localized strings from " << path.str() <<
228                ". Error: " << e.getFormattedMessage());
229         return false;
230     }
231
232    return true;
233 }
234
235 // Load strings for requested resource (for current and default locale).
236 // Result is stored below "strings" in the property tree of the locales.
237 bool
238 FGLocale::loadResource(const char* resource)
239 {
240     // load defaults first
241     bool Ok = loadResource(_defaultLocale, resource);
242
243     // also load language specific resource, unless identical
244     if ((_currentLocale!=0)&&
245         (_defaultLocale != _currentLocale))
246     {
247         Ok &= loadResource(_currentLocale, resource);
248     }
249
250     return Ok;
251 }
252
253 const char*
254 FGLocale::getLocalizedString(SGPropertyNode *localeNode, const char* id, const char* context)
255 {
256     SGPropertyNode *n = localeNode->getNode("strings",0, true)->getNode(context);
257     if (n)
258         return n->getStringValue(id, NULL);
259     return NULL;
260 }
261
262 const char*
263 FGLocale::getLocalizedString(const char* id, const char* resource, const char* Default)
264 {
265     if (id && resource)
266     {
267         const char* s = NULL;
268         if (_currentLocale)
269             s = getLocalizedString(_currentLocale, id, resource);
270         if (s && s[0]!=0)
271             return s;
272
273         if (_defaultLocale)
274             s = getLocalizedString(_defaultLocale, id, resource);
275         if (s && s[0]!=0)
276             return s;
277     }
278     return Default;
279 }
280
281 simgear::PropertyList
282 FGLocale::getLocalizedStrings(SGPropertyNode *localeNode, const char* id, const char* context)
283 {
284     SGPropertyNode *n = localeNode->getNode("strings",0, true)->getNode(context);
285     if (n)
286     {
287         return n->getChildren(id);
288     }
289     return simgear::PropertyList();
290 }
291
292 simgear::PropertyList
293 FGLocale::getLocalizedStrings(const char* id, const char* resource)
294 {
295     if (id && resource)
296     {
297         if (_currentLocale)
298         {
299             simgear::PropertyList s = getLocalizedStrings(_currentLocale, id, resource);
300             if (! s.empty())
301                 return s;
302         }
303
304         if (_defaultLocale)
305         {
306             simgear::PropertyList s = getLocalizedStrings(_defaultLocale, id, resource);
307             if (! s.empty())
308                 return s;
309         }
310     }
311     return simgear::PropertyList();
312 }
313
314 // Check for localized font
315 const char*
316 FGLocale::getDefaultFont(const char* fallbackFont)
317 {
318     const char* font = NULL;
319     if (_currentLocale)
320     {
321         font = _currentLocale->getStringValue("font", "");
322         if (font[0] != 0)
323             return font;
324     }
325     if (_defaultLocale)
326     {
327         font = _defaultLocale->getStringValue("font", "");
328         if (font[0] != 0)
329             return font;
330     }
331
332     return fallbackFont;
333 }
334
335 std::string FGLocale::localizedPrintf(const char* id, const char* resource, ... )
336 {
337     va_list args;
338     va_start(args, resource);
339     string r = vlocalizedPrintf(id, resource, args);
340     va_end(args);
341     return r;
342 }
343
344 std::string FGLocale::vlocalizedPrintf(const char* id, const char* resource, va_list args)
345 {
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);
351 }
352
353 // Simple UTF8 to Latin1 encoder.
354 void FGLocale::utf8toLatin1(string& s)
355 {
356     size_t pos = 0;
357
358     // map '0xc3..' utf8 characters to Latin1
359     while ((string::npos != (pos = s.find('\xc3',pos)))&&
360            (pos+1 < s.size()))
361     {
362         char c='*';
363         unsigned char p = s[pos+1];
364         if ((p>=0x80)&&(p<0xc0))
365             c = 0x40 + p;
366         char v[2];
367         v[0]=c;
368         v[1]=0;
369         s.replace(pos, 2, v);
370         pos++;
371     }
372
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]);
377     printf("\n");
378 #endif
379
380     // hack: also map some Latin2 characters to plain-text ASCII
381     pos = 0;
382     while ((string::npos != (pos = s.find('\xc5',pos)))&&
383            (pos+1 < s.size()))
384     {
385         char c='*';
386         unsigned char p = s[pos+1];
387         switch(p)
388         {
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;
394         }
395         char v[2];
396         v[0]=c;
397         v[1]=0;
398         s.replace(pos, 2, v);
399         pos++;
400     }
401 }
402
403 const char* fgTrMsg(const char* key)
404 {
405     return globals->get_locale()->getLocalizedString(key, "message");
406 }
407
408 std::string fgTrPrintfMsg(const char* key, ...)
409 {
410     va_list args;
411     va_start(args, key);
412     string r = globals->get_locale()->vlocalizedPrintf(key, "message", args);
413     va_end(args);
414     return r;
415     
416 }