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