]> git.mxchange.org Git - flightgear.git/blob - src/Main/locale.cxx
Structural work (init + shutdown) for new reset system.
[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
94 // determine locale / langauge on Mac
95 #include <CoreFoundation/CoreFoundation.h>
96
97 string_list
98 FGLocale::getUserLanguage()
99 {
100     string_list result;
101     CFArrayRef langs = CFLocaleCopyPreferredLanguages();
102     
103     char buffer[64];
104     for (int i=0; i<CFArrayGetCount(langs); ++i) {
105         CFStringRef s = (CFStringRef) CFArrayGetValueAtIndex(langs, i);
106         CFStringGetCString(s, buffer, 64, kCFStringEncodingASCII);
107         result.push_back(buffer);
108     }
109     
110     CFRelease(langs);
111     return result;
112 }
113
114 #else
115 /**
116  * Determine locale/language settings on Linux/Unix.
117  */
118 string_list
119 FGLocale::getUserLanguage()
120 {
121     string_list result;
122     const char* langEnv = ::getenv("LANG");
123     if (langEnv) {
124         result.push_back(langEnv);
125     }
126     
127     return result;
128 }
129 #endif
130
131 // Search property tree for matching locale description
132 SGPropertyNode*
133 FGLocale::findLocaleNode(const string& language)
134 {
135     SGPropertyNode* node = NULL;
136
137     // remove character encoding from the locale spec, i.e. "de_DE.utf8" => "de_DE"
138     size_t pos = language.find(".");
139     if ((pos != string::npos)&&(pos>0))
140     {
141         node = findLocaleNode(language.substr(0, pos));
142         if (node)
143             return node;
144     }
145
146     SG_LOG(SG_GENERAL, SG_DEBUG, "Searching language resource for locale: " << language);
147     // search locale using full string
148     vector<SGPropertyNode_ptr> localeList = _intl->getChildren("locale");
149
150     for (size_t i = 0; i < localeList.size(); i++)
151     {
152        vector<SGPropertyNode_ptr> langList = localeList[i]->getChildren("lang");
153
154        for (size_t j = 0; j < langList.size(); j++)
155        {
156            if (!language.compare(langList[j]->getStringValue()))
157            {
158                SG_LOG(SG_GENERAL, SG_INFO, "Found language resource for: " << language);
159                return localeList[i];
160            }
161        }
162     }
163
164     // try country's default resource, i.e. "de_DE" => "de"
165     pos = language.find("_");
166     if ((pos != string::npos)&&(pos>0))
167     {
168         node = findLocaleNode(language.substr(0, pos));
169         if (node)
170             return node;
171     }
172
173     return NULL;
174 }
175
176 // Select the language. When no language is given (NULL),
177 // a default is determined matching the system locale.
178 bool
179 FGLocale::selectLanguage(const char *language)
180 {
181     string_list languages = getUserLanguage();
182     if (languages.empty()) {
183         // Use plain C locale if nothing is available.
184         SG_LOG(SG_GENERAL, SG_WARN, "Unable to detect system language" );
185         languages.push_back("C");
186     }
187     
188     // if we were passed a language option, try it first
189     if ((language != NULL) && (strlen(language) > 0)) {
190         languages.insert(languages.begin(), string(language));
191     }
192
193     
194     SGPropertyNode *locale = NULL;
195     BOOST_FOREACH(string lang, languages) {
196         locale = findLocaleNode(lang);
197         if (locale) {
198             break;
199         }
200     }
201     
202     if (!locale)
203     {
204        SG_LOG(SG_GENERAL, SG_ALERT,
205               "No internationalization settings specified in preferences.xml" );
206        return false;
207     }
208
209     _currentLocale = locale;
210
211     // load resource for system messages (translations for fgfs internal messages)
212     loadResource("sys");
213
214     return true;
215 }
216
217 // Load strings for requested resource and locale.
218 // Result is stored below "strings" in the property tree of the given locale.
219 bool
220 FGLocale::loadResource(SGPropertyNode* localeNode, const char* resource)
221 {
222     SGPath path( globals->get_fg_root() );
223
224     SGPropertyNode* stringNode = localeNode->getNode("strings", 0, true);
225
226     const char *path_str = stringNode->getStringValue(resource, NULL);
227     if (!path_str)
228     {
229         SG_LOG(SG_GENERAL, SG_WARN, "No path in " << stringNode->getPath() << "/" << resource << ".");
230         return NULL;
231     }
232
233     path.append(path_str);
234     SG_LOG(SG_GENERAL, SG_INFO, "Reading localized strings for '" <<
235            localeNode->getStringValue("lang", "<none>")
236            <<"' from " << path.str());
237
238     // load the actual file
239     try
240     {
241         readProperties(path.str(), stringNode->getNode(resource, 0, true));
242     } catch (const sg_exception &e)
243     {
244         SG_LOG(SG_GENERAL, SG_ALERT, "Unable to read the localized strings from " << path.str() <<
245                ". Error: " << e.getFormattedMessage());
246         return false;
247     }
248
249    return true;
250 }
251
252 // Load strings for requested resource (for current and default locale).
253 // Result is stored below "strings" in the property tree of the locales.
254 bool
255 FGLocale::loadResource(const char* resource)
256 {
257     // load defaults first
258     bool Ok = loadResource(_defaultLocale, resource);
259
260     // also load language specific resource, unless identical
261     if ((_currentLocale!=0)&&
262         (_defaultLocale != _currentLocale))
263     {
264         Ok &= loadResource(_currentLocale, resource);
265     }
266
267     return Ok;
268 }
269
270 const char*
271 FGLocale::getLocalizedString(SGPropertyNode *localeNode, const char* id, const char* context)
272 {
273     SGPropertyNode *n = localeNode->getNode("strings",0, true)->getNode(context);
274     if (n)
275         return n->getStringValue(id, NULL);
276     return NULL;
277 }
278
279 const char*
280 FGLocale::getLocalizedString(const char* id, const char* resource, const char* Default)
281 {
282     if (id && resource)
283     {
284         const char* s = NULL;
285         if (_currentLocale)
286             s = getLocalizedString(_currentLocale, id, resource);
287         if (s && s[0]!=0)
288             return s;
289
290         if (_defaultLocale)
291             s = getLocalizedString(_defaultLocale, id, resource);
292         if (s && s[0]!=0)
293             return s;
294     }
295     return Default;
296 }
297
298 simgear::PropertyList
299 FGLocale::getLocalizedStrings(SGPropertyNode *localeNode, const char* id, const char* context)
300 {
301     SGPropertyNode *n = localeNode->getNode("strings",0, true)->getNode(context);
302     if (n)
303     {
304         return n->getChildren(id);
305     }
306     return simgear::PropertyList();
307 }
308
309 simgear::PropertyList
310 FGLocale::getLocalizedStrings(const char* id, const char* resource)
311 {
312     if (id && resource)
313     {
314         if (_currentLocale)
315         {
316             simgear::PropertyList s = getLocalizedStrings(_currentLocale, id, resource);
317             if (! s.empty())
318                 return s;
319         }
320
321         if (_defaultLocale)
322         {
323             simgear::PropertyList s = getLocalizedStrings(_defaultLocale, id, resource);
324             if (! s.empty())
325                 return s;
326         }
327     }
328     return simgear::PropertyList();
329 }
330
331 // Check for localized font
332 const char*
333 FGLocale::getDefaultFont(const char* fallbackFont)
334 {
335     const char* font = NULL;
336     if (_currentLocale)
337     {
338         font = _currentLocale->getStringValue("font", "");
339         if (font[0] != 0)
340             return font;
341     }
342     if (_defaultLocale)
343     {
344         font = _defaultLocale->getStringValue("font", "");
345         if (font[0] != 0)
346             return font;
347     }
348
349     return fallbackFont;
350 }
351
352 std::string FGLocale::localizedPrintf(const char* id, const char* resource, ... )
353 {
354     va_list args;
355     va_start(args, resource);
356     string r = vlocalizedPrintf(id, resource, args);
357     va_end(args);
358     return r;
359 }
360
361 std::string FGLocale::vlocalizedPrintf(const char* id, const char* resource, va_list args)
362 {
363     const char* format = getLocalizedString(id, resource);
364     int len = ::vsprintf(NULL, format, args);
365     char* buf = (char*) alloca(len);
366     ::vsprintf(buf, format, args);
367     return std::string(buf);
368 }
369
370 // Simple UTF8 to Latin1 encoder.
371 void FGLocale::utf8toLatin1(string& s)
372 {
373     size_t pos = 0;
374
375     // map '0xc3..' utf8 characters to Latin1
376     while ((string::npos != (pos = s.find('\xc3',pos)))&&
377            (pos+1 < s.size()))
378     {
379         char c='*';
380         unsigned char p = s[pos+1];
381         if ((p>=0x80)&&(p<0xc0))
382             c = 0x40 + p;
383         char v[2];
384         v[0]=c;
385         v[1]=0;
386         s.replace(pos, 2, v);
387         pos++;
388     }
389
390 #ifdef DEBUG_ENCODING
391     printf("'%s': ", s.c_str());
392     for (pos = 0;pos<s.size();pos++)
393         printf("%02x ", (unsigned char) s[pos]);
394     printf("\n");
395 #endif
396
397     // hack: also map some Latin2 characters to plain-text ASCII
398     pos = 0;
399     while ((string::npos != (pos = s.find('\xc5',pos)))&&
400            (pos+1 < s.size()))
401     {
402         char c='*';
403         unsigned char p = s[pos+1];
404         switch(p)
405         {
406             case 0x82:c='l';break;
407             case 0x9a:c='S';break;
408             case 0x9b:c='s';break;
409             case 0xba:c='z';break;
410             case 0xbc:c='z';break;
411         }
412         char v[2];
413         v[0]=c;
414         v[1]=0;
415         s.replace(pos, 2, v);
416         pos++;
417     }
418 }
419
420 const char* fgTrMsg(const char* key)
421 {
422     return globals->get_locale()->getLocalizedString(key, "message");
423 }
424
425 std::string fgTrPrintfMsg(const char* key, ...)
426 {
427     va_list args;
428     va_start(args, key);
429     string r = globals->get_locale()->vlocalizedPrintf(key, "message", args);
430     va_end(args);
431     return r;
432     
433 }