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