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