]> git.mxchange.org Git - flightgear.git/blob - src/Main/locale.cxx
Reduce amount of log output at level=debug.
[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 <boost/foreach.hpp>
30
31 #include <simgear/props/props_io.hxx>
32 #include <simgear/structure/exception.hxx>
33
34 #include "fg_props.hxx"
35 #include "locale.hxx"
36
37 using std::vector;
38 using std::string;
39
40 FGLocale::FGLocale(SGPropertyNode* root) :
41     _intl(root->getNode("/sim/intl",0, true)),
42     _defaultLocale(_intl->getChild("locale",0, true))
43 {
44 }
45
46 FGLocale::~FGLocale()
47 {
48 }
49
50 #ifdef _WIN32
51 /**
52  * Determine locale/language settings on Windows.
53  *
54  * Copyright (C) 1997, 2002, 2003 Martin von Loewis
55  *
56  * Permission to use, copy, modify, and distribute this software and its
57  * documentation for any purpose and without fee is hereby granted,
58  * provided that the above copyright notice appear in all copies.
59  *
60  * This software comes with no warranty. Use at your own risk.
61  */
62 string_list
63 FGLocale::getUserLanguage()
64 {
65     string_list result;
66     static char locale[100] = {0};
67
68     if (GetLocaleInfo(LOCALE_USER_DEFAULT,
69                       LOCALE_SISO639LANGNAME,
70                       locale, sizeof(locale)))
71     {
72         SG_LOG(SG_GENERAL, SG_DEBUG, "Detected locale's language setting: " << locale);
73         size_t i = strlen(locale);
74         locale[i++] = '_';
75         if (GetLocaleInfo(LOCALE_USER_DEFAULT,
76                           LOCALE_SISO3166CTRYNAME,
77                           locale+i, (int)(sizeof(locale)-i)))
78         {
79             result.push_back(locale);
80             return result;
81         }
82         
83         locale[--i] = 0;
84         SG_LOG(SG_GENERAL, SG_WARN, "Failed to detected locale's country setting.");
85         result.push_back(locale);
86         return result;
87     }
88
89     return result;
90 }
91 #elif __APPLE__
92
93 // determine locale / langauge on Mac
94 #include <CoreFoundation/CoreFoundation.h>
95
96 string_list
97 FGLocale::getUserLanguage()
98 {
99     string_list result;
100     CFArrayRef langs = CFLocaleCopyPreferredLanguages();
101     
102     char buffer[64];
103     for (int i=0; i<CFArrayGetCount(langs); ++i) {
104         CFStringRef s = (CFStringRef) CFArrayGetValueAtIndex(langs, i);
105         CFStringGetCString(s, buffer, 64, kCFStringEncodingASCII);
106         result.push_back(buffer);
107     }
108     
109     CFRelease(langs);
110     return result;
111 }
112
113 #else
114 /**
115  * Determine locale/language settings on Linux/Unix.
116  */
117 string_list
118 FGLocale::getUserLanguage()
119 {
120     string_list result;
121     const char* langEnv = ::getenv("LANG");
122     if (langEnv) {
123         result.push_back(langEnv);
124     }
125     
126     return result;
127 }
128 #endif
129
130 // Search property tree for matching locale description
131 SGPropertyNode*
132 FGLocale::findLocaleNode(const string& language)
133 {
134     SGPropertyNode* node = NULL;
135
136     // remove character encoding from the locale spec, i.e. "de_DE.utf8" => "de_DE"
137     size_t pos = language.find(".");
138     if ((pos != string::npos)&&(pos>0))
139     {
140         node = findLocaleNode(language.substr(0, pos));
141         if (node)
142             return node;
143     }
144
145     SG_LOG(SG_GENERAL, SG_DEBUG, "Searching language resource for locale: " << language);
146     // search locale using full string
147     vector<SGPropertyNode_ptr> localeList = _intl->getChildren("locale");
148
149     for (size_t i = 0; i < localeList.size(); i++)
150     {
151        vector<SGPropertyNode_ptr> langList = localeList[i]->getChildren("lang");
152
153        for (size_t j = 0; j < langList.size(); j++)
154        {
155            if (!language.compare(langList[j]->getStringValue()))
156            {
157                SG_LOG(SG_GENERAL, SG_INFO, "Found language resource for: " << language);
158                return localeList[i];
159            }
160        }
161     }
162
163     // try country's default resource, i.e. "de_DE" => "de"
164     pos = language.find("_");
165     if ((pos != string::npos)&&(pos>0))
166     {
167         node = findLocaleNode(language.substr(0, pos));
168         if (node)
169             return node;
170     }
171
172     return NULL;
173 }
174
175 // Select the language. When no language is given (NULL),
176 // a default is determined matching the system locale.
177 bool
178 FGLocale::selectLanguage(const char *language)
179 {
180     string_list languages = getUserLanguage();
181     if (languages.empty()) {
182         // Use plain C locale if nothing is available.
183         SG_LOG(SG_GENERAL, SG_WARN, "Unable to detect system language" );
184         languages.push_back("C");
185     }
186     
187     // if we were passed a language option, try it first
188     if ((language != NULL) && (strlen(language) > 0)) {
189         languages.insert(languages.begin(), string(language));
190     }
191
192     
193     SGPropertyNode *locale = NULL;
194     BOOST_FOREACH(string lang, languages) {
195         locale = findLocaleNode(lang);
196         if (locale) {
197             break;
198         }
199     }
200     
201     if (!locale)
202     {
203        SG_LOG(SG_GENERAL, SG_ALERT,
204               "No internationalization settings specified in preferences.xml" );
205        return false;
206     }
207
208     _currentLocale = locale;
209
210     // load resource for system messages (translations for fgfs internal messages)
211     loadResource("sys");
212
213     return true;
214 }
215
216 // Load strings for requested resource and locale.
217 // Result is stored below "strings" in the property tree of the given locale.
218 bool
219 FGLocale::loadResource(SGPropertyNode* localeNode, const char* resource)
220 {
221     SGPath path( globals->get_fg_root() );
222
223     SGPropertyNode* stringNode = localeNode->getNode("strings", 0, true);
224
225     const char *path_str = stringNode->getStringValue(resource, NULL);
226     if (!path_str)
227     {
228         SG_LOG(SG_GENERAL, SG_WARN, "No path in " << stringNode->getPath() << "/" << resource << ".");
229         return NULL;
230     }
231
232     path.append(path_str);
233     SG_LOG(SG_GENERAL, SG_INFO, "Reading localized strings for '" <<
234            localeNode->getStringValue("lang", "<none>")
235            <<"' from " << path.str());
236
237     // load the actual file
238     try
239     {
240         readProperties(path.str(), stringNode->getNode(resource, 0, true));
241     } catch (const sg_exception &e)
242     {
243         SG_LOG(SG_GENERAL, SG_ALERT, "Unable to read the localized strings from " << path.str() <<
244                ". Error: " << e.getFormattedMessage());
245         return false;
246     }
247
248    return true;
249 }
250
251 // Load strings for requested resource (for current and default locale).
252 // Result is stored below "strings" in the property tree of the locales.
253 bool
254 FGLocale::loadResource(const char* resource)
255 {
256     // load defaults first
257     bool Ok = loadResource(_defaultLocale, resource);
258
259     // also load language specific resource, unless identical
260     if ((_currentLocale!=0)&&
261         (_defaultLocale != _currentLocale))
262     {
263         Ok &= loadResource(_currentLocale, resource);
264     }
265
266     return Ok;
267 }
268
269 const char*
270 FGLocale::getLocalizedString(SGPropertyNode *localeNode, const char* id, const char* context)
271 {
272     SGPropertyNode *n = localeNode->getNode("strings",0, true)->getNode(context);
273     if (n)
274         return n->getStringValue(id, NULL);
275     return NULL;
276 }
277
278 const char*
279 FGLocale::getLocalizedString(const char* id, const char* resource, const char* Default)
280 {
281     if (id && resource)
282     {
283         const char* s = NULL;
284         if (_currentLocale)
285             s = getLocalizedString(_currentLocale, id, resource);
286         if (s && s[0]!=0)
287             return s;
288
289         if (_defaultLocale)
290             s = getLocalizedString(_defaultLocale, id, resource);
291         if (s && s[0]!=0)
292             return s;
293     }
294     return Default;
295 }
296
297 simgear::PropertyList
298 FGLocale::getLocalizedStrings(SGPropertyNode *localeNode, const char* id, const char* context)
299 {
300     SGPropertyNode *n = localeNode->getNode("strings",0, true)->getNode(context);
301     if (n)
302     {
303         return n->getChildren(id);
304     }
305     return simgear::PropertyList();
306 }
307
308 simgear::PropertyList
309 FGLocale::getLocalizedStrings(const char* id, const char* resource)
310 {
311     if (id && resource)
312     {
313         if (_currentLocale)
314         {
315             simgear::PropertyList s = getLocalizedStrings(_currentLocale, id, resource);
316             if (! s.empty())
317                 return s;
318         }
319
320         if (_defaultLocale)
321         {
322             simgear::PropertyList s = getLocalizedStrings(_defaultLocale, id, resource);
323             if (! s.empty())
324                 return s;
325         }
326     }
327     return simgear::PropertyList();
328 }
329
330 // Check for localized font
331 const char*
332 FGLocale::getDefaultFont(const char* fallbackFont)
333 {
334     const char* font = NULL;
335     if (_currentLocale)
336     {
337         font = _currentLocale->getStringValue("font", "");
338         if (font[0] != 0)
339             return font;
340     }
341     if (_defaultLocale)
342     {
343         font = _defaultLocale->getStringValue("font", "");
344         if (font[0] != 0)
345             return font;
346     }
347
348     return fallbackFont;
349 }
350
351 // Simple UTF8 to Latin1 encoder.
352 void FGLocale::utf8toLatin1(string& s)
353 {
354     size_t pos = 0;
355
356     // map '0xc3..' utf8 characters to Latin1
357     while ((string::npos != (pos = s.find('\xc3',pos)))&&
358            (pos+1 < s.size()))
359     {
360         char c='*';
361         unsigned char p = s[pos+1];
362         if ((p>=0x80)&&(p<0xc0))
363             c = 0x40 + p;
364         char v[2];
365         v[0]=c;
366         v[1]=0;
367         s.replace(pos, 2, v);
368         pos++;
369     }
370
371 #ifdef DEBUG_ENCODING
372     printf("'%s': ", s.c_str());
373     for (pos = 0;pos<s.size();pos++)
374         printf("%02x ", (unsigned char) s[pos]);
375     printf("\n");
376 #endif
377
378     // hack: also map some Latin2 characters to plain-text ASCII
379     pos = 0;
380     while ((string::npos != (pos = s.find('\xc5',pos)))&&
381            (pos+1 < s.size()))
382     {
383         char c='*';
384         unsigned char p = s[pos+1];
385         switch(p)
386         {
387             case 0x82:c='l';break;
388             case 0x9a:c='S';break;
389             case 0x9b:c='s';break;
390             case 0xba:c='z';break;
391             case 0xbc:c='z';break;
392         }
393         char v[2];
394         v[0]=c;
395         v[1]=0;
396         s.replace(pos, 2, v);
397         pos++;
398     }
399 }