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