]> git.mxchange.org Git - flightgear.git/blob - src/Main/locale.cxx
Reset: clear effects cache
[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     SGPropertyNode *locale = NULL;
175     BOOST_FOREACH(string lang, languages) {
176         locale = findLocaleNode(lang);
177         if (locale) {
178             break;
179         }
180     }
181     
182     if (!locale)
183     {
184        SG_LOG(SG_GENERAL, SG_ALERT,
185               "No internationalization settings specified in preferences.xml" );
186        return false;
187     }
188
189     _currentLocale = locale;
190
191     // load resource for system messages (translations for fgfs internal messages)
192     loadResource("sys");
193
194     return true;
195 }
196
197 // Load strings for requested resource and locale.
198 // Result is stored below "strings" in the property tree of the given locale.
199 bool
200 FGLocale::loadResource(SGPropertyNode* localeNode, const char* resource)
201 {
202     SGPath path( globals->get_fg_root() );
203
204     SGPropertyNode* stringNode = localeNode->getNode("strings", 0, true);
205
206     const char *path_str = stringNode->getStringValue(resource, NULL);
207     if (!path_str)
208     {
209         SG_LOG(SG_GENERAL, SG_WARN, "No path in " << stringNode->getPath() << "/" << resource << ".");
210         return NULL;
211     }
212
213     path.append(path_str);
214     SG_LOG(SG_GENERAL, SG_INFO, "Reading localized strings for '" <<
215            localeNode->getStringValue("lang", "<none>")
216            <<"' from " << path.str());
217
218     // load the actual file
219     try
220     {
221         readProperties(path.str(), stringNode->getNode(resource, 0, true));
222     } catch (const sg_exception &e)
223     {
224         SG_LOG(SG_GENERAL, SG_ALERT, "Unable to read the localized strings from " << path.str() <<
225                ". Error: " << e.getFormattedMessage());
226         return false;
227     }
228
229    return true;
230 }
231
232 // Load strings for requested resource (for current and default locale).
233 // Result is stored below "strings" in the property tree of the locales.
234 bool
235 FGLocale::loadResource(const char* resource)
236 {
237     // load defaults first
238     bool Ok = loadResource(_defaultLocale, resource);
239
240     // also load language specific resource, unless identical
241     if ((_currentLocale!=0)&&
242         (_defaultLocale != _currentLocale))
243     {
244         Ok &= loadResource(_currentLocale, resource);
245     }
246
247     return Ok;
248 }
249
250 const char*
251 FGLocale::getLocalizedString(SGPropertyNode *localeNode, const char* id, const char* context)
252 {
253     SGPropertyNode *n = localeNode->getNode("strings",0, true)->getNode(context);
254     if (n)
255         return n->getStringValue(id, NULL);
256     return NULL;
257 }
258
259 const char*
260 FGLocale::getLocalizedString(const char* id, const char* resource, const char* Default)
261 {
262     if (id && resource)
263     {
264         const char* s = NULL;
265         if (_currentLocale)
266             s = getLocalizedString(_currentLocale, id, resource);
267         if (s && s[0]!=0)
268             return s;
269
270         if (_defaultLocale)
271             s = getLocalizedString(_defaultLocale, id, resource);
272         if (s && s[0]!=0)
273             return s;
274     }
275     return Default;
276 }
277
278 simgear::PropertyList
279 FGLocale::getLocalizedStrings(SGPropertyNode *localeNode, const char* id, const char* context)
280 {
281     SGPropertyNode *n = localeNode->getNode("strings",0, true)->getNode(context);
282     if (n)
283     {
284         return n->getChildren(id);
285     }
286     return simgear::PropertyList();
287 }
288
289 simgear::PropertyList
290 FGLocale::getLocalizedStrings(const char* id, const char* resource)
291 {
292     if (id && resource)
293     {
294         if (_currentLocale)
295         {
296             simgear::PropertyList s = getLocalizedStrings(_currentLocale, id, resource);
297             if (! s.empty())
298                 return s;
299         }
300
301         if (_defaultLocale)
302         {
303             simgear::PropertyList s = getLocalizedStrings(_defaultLocale, id, resource);
304             if (! s.empty())
305                 return s;
306         }
307     }
308     return simgear::PropertyList();
309 }
310
311 // Check for localized font
312 const char*
313 FGLocale::getDefaultFont(const char* fallbackFont)
314 {
315     const char* font = NULL;
316     if (_currentLocale)
317     {
318         font = _currentLocale->getStringValue("font", "");
319         if (font[0] != 0)
320             return font;
321     }
322     if (_defaultLocale)
323     {
324         font = _defaultLocale->getStringValue("font", "");
325         if (font[0] != 0)
326             return font;
327     }
328
329     return fallbackFont;
330 }
331
332 std::string FGLocale::localizedPrintf(const char* id, const char* resource, ... )
333 {
334     va_list args;
335     va_start(args, resource);
336     string r = vlocalizedPrintf(id, resource, args);
337     va_end(args);
338     return r;
339 }
340
341 std::string FGLocale::vlocalizedPrintf(const char* id, const char* resource, va_list args)
342 {
343     const char* format = getLocalizedString(id, resource);
344     int len = ::vsprintf(NULL, format, args);
345     char* buf = (char*) alloca(len);
346     ::vsprintf(buf, format, args);
347     return std::string(buf);
348 }
349
350 // Simple UTF8 to Latin1 encoder.
351 void FGLocale::utf8toLatin1(string& s)
352 {
353     size_t pos = 0;
354
355     // map '0xc3..' utf8 characters to Latin1
356     while ((string::npos != (pos = s.find('\xc3',pos)))&&
357            (pos+1 < s.size()))
358     {
359         char c='*';
360         unsigned char p = s[pos+1];
361         if ((p>=0x80)&&(p<0xc0))
362             c = 0x40 + p;
363         char v[2];
364         v[0]=c;
365         v[1]=0;
366         s.replace(pos, 2, v);
367         pos++;
368     }
369
370 #ifdef DEBUG_ENCODING
371     printf("'%s': ", s.c_str());
372     for (pos = 0;pos<s.size();pos++)
373         printf("%02x ", (unsigned char) s[pos]);
374     printf("\n");
375 #endif
376
377     // hack: also map some Latin2 characters to plain-text ASCII
378     pos = 0;
379     while ((string::npos != (pos = s.find('\xc5',pos)))&&
380            (pos+1 < s.size()))
381     {
382         char c='*';
383         unsigned char p = s[pos+1];
384         switch(p)
385         {
386             case 0x82:c='l';break;
387             case 0x9a:c='S';break;
388             case 0x9b:c='s';break;
389             case 0xba:c='z';break;
390             case 0xbc:c='z';break;
391         }
392         char v[2];
393         v[0]=c;
394         v[1]=0;
395         s.replace(pos, 2, v);
396         pos++;
397     }
398 }
399
400 const char* fgTrMsg(const char* key)
401 {
402     return globals->get_locale()->getLocalizedString(key, "message");
403 }
404
405 std::string fgTrPrintfMsg(const char* key, ...)
406 {
407     va_list args;
408     va_start(args, key);
409     string r = globals->get_locale()->vlocalizedPrintf(key, "message", args);
410     va_end(args);
411     return r;
412     
413 }