]> git.mxchange.org Git - flightgear.git/blob - src/Main/locale.cxx
Move locale code to separate module.
[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., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21 #include <simgear/props/props_io.hxx>
22 #include <simgear/structure/exception.hxx>
23
24 #include "fg_props.hxx"
25 #include "locale.hxx"
26
27 using std::vector;
28 using std::string;
29
30 FGLocale::FGLocale(SGPropertyNode* root) :
31     _intl(root->getNode("/sim/intl",0, true)),
32     _defaultLocale(_intl->getChild("locale",0, true))
33 {
34 }
35
36 FGLocale::~FGLocale()
37 {
38 }
39
40 // Search property tree for matching locale description
41 SGPropertyNode*
42 FGLocale::findLocaleNode(const string& language)
43 {
44     SG_LOG(SG_GENERAL, SG_INFO, "Searching language resource for locale: " << language);
45
46     SGPropertyNode* node = NULL;
47
48     // remove character encoding from the locale spec, i.e. "de_DE.utf8" => "de_DE"
49     size_t pos = language.find(".");
50     if ((pos != string::npos)&&(pos>0))
51     {
52         node = findLocaleNode(language.substr(0, pos));
53         if (node)
54             return node;
55     }
56
57     // try country's default resource, i.e. "de_DE" => "de"
58     pos = language.find("_");
59     if ((pos != string::npos)&&(pos>0))
60     {
61         node = findLocaleNode(language.substr(0, pos));
62         if (node)
63             return node;
64     }
65
66     // search locale using full string
67     vector<SGPropertyNode_ptr> localeList = _intl->getChildren("locale");
68
69     for (size_t i = 0; i < localeList.size(); i++)
70     {
71        vector<SGPropertyNode_ptr> langList = localeList[i]->getChildren("lang");
72
73        for (size_t j = 0; j < langList.size(); j++)
74        {
75           if (!language.compare(langList[j]->getStringValue()))
76              return localeList[i];
77        }
78     }
79
80     return NULL;
81 }
82
83 // Select the language. When no language is given (NULL),
84 // a default is determined matching the system locale.
85 bool
86 FGLocale::selectLanguage(const char *language)
87 {
88     // Use environment setting when no language is given.
89     if ((language == NULL)||(language[0]==0))
90         language = ::getenv("LANG");
91
92     // Use plain C locale if nothing is available.
93     if (language == NULL)
94     {
95         SG_LOG(SG_GENERAL, SG_INFO, "Unable to detect the language" );
96         language = "C";
97     }
98
99     SGPropertyNode *locale = findLocaleNode(language);
100     if (!locale)
101     {
102        SG_LOG(SG_GENERAL, SG_ALERT,
103               "No internationalization settings specified in preferences.xml" );
104        return false;
105     }
106
107     _currentLocale = locale;
108     return true;
109 }
110
111 // Load strings for requested resource and locale.
112 // Result is stored below "strings" in the property tree of the given locale.
113 bool
114 FGLocale::loadResource(SGPropertyNode* localeNode, const char* resource)
115 {
116     SGPath path( globals->get_fg_root() );
117
118     SGPropertyNode* stringNode = localeNode->getNode("strings", 0, true);
119
120     const char *path_str = stringNode->getStringValue(resource, NULL);
121     if (!path_str)
122     {
123         SG_LOG(SG_GENERAL, SG_ALERT, "No path in " << stringNode->getPath() << "/" << resource << ".");
124         return NULL;
125     }
126
127     path.append(path_str);
128     SG_LOG(SG_GENERAL, SG_INFO, "Reading localized strings for '" <<
129            localeNode->getStringValue("lang", "<none>")
130            <<"' from " << path.str());
131
132     // load the actual file
133     try
134     {
135         readProperties(path.str(), stringNode->getNode(resource, 0, true));
136     } catch (const sg_exception &e)
137     {
138         SG_LOG(SG_GENERAL, SG_ALERT, "Unable to read the localized strings from " << path.str() <<
139                ". Error: " << e.getFormattedMessage());
140         return false;
141     }
142
143    return true;
144 }
145
146 // Load strings for requested resource (for current and default locale).
147 // Result is stored below "strings" in the property tree of the locales.
148 bool
149 FGLocale::loadResource(const char* resource)
150 {
151     // load defaults first
152     bool Ok = loadResource(_defaultLocale, resource);
153
154     // also load language specific resource, unless identical
155     if ((_currentLocale!=0)&&
156         (_defaultLocale != _currentLocale))
157     {
158         Ok &= loadResource(_currentLocale, resource);
159     }
160
161     return Ok;
162 }
163
164 const char*
165 FGLocale::getLocalizedString(SGPropertyNode *localeNode, const char* id, const char* context)
166 {
167     SGPropertyNode *n = localeNode->getNode("strings",0, true)->getNode(context);
168     if (n)
169         return n->getStringValue(id, NULL);
170     return NULL;
171 }
172
173 const char*
174 FGLocale::getLocalizedString(const char* id, const char* resource, const char* Default)
175 {
176     if (id && resource)
177     {
178         const char* s = NULL;
179         if (_currentLocale)
180             s = getLocalizedString(_currentLocale, id, resource);
181         if (s && s[0]!=0)
182             return s;
183
184         if (_defaultLocale)
185             s = getLocalizedString(_defaultLocale, id, resource);
186         if (s && s[0]!=0)
187             return s;
188     }
189     return Default;
190 }
191
192 simgear::PropertyList
193 FGLocale::getLocalizedStrings(SGPropertyNode *localeNode, const char* id, const char* context)
194 {
195     SGPropertyNode *n = localeNode->getNode("strings",0, true)->getNode(context);
196     if (n)
197     {
198         return n->getChildren(id);
199     }
200     return simgear::PropertyList();
201 }
202
203 simgear::PropertyList
204 FGLocale::getLocalizedStrings(const char* id, const char* resource)
205 {
206     if (id && resource)
207     {
208         if (_currentLocale)
209         {
210             simgear::PropertyList s = getLocalizedStrings(_currentLocale, id, resource);
211             if (s.size())
212                 return s;
213         }
214
215         if (_defaultLocale)
216         {
217             simgear::PropertyList s = getLocalizedStrings(_defaultLocale, id, resource);
218             if (s.size())
219                 return s;
220         }
221     }
222     return simgear::PropertyList();
223 }
224
225 // Check for localized font
226 const char*
227 FGLocale::getDefaultFont(const char* fallbackFont)
228 {
229     const char* font = NULL;
230     if (_currentLocale)
231     {
232         font = _currentLocale->getStringValue("font", "");
233         if (font[0] != 0)
234             return font;
235     }
236     if (_defaultLocale)
237     {
238         font = _defaultLocale->getStringValue("font", "");
239         if (font[0] != 0)
240             return font;
241     }
242
243     return fallbackFont;
244 }
245
246 // Simple UTF8 to Latin1 encoder.
247 void FGLocale::utf8toLatin1(string& s)
248 {
249     size_t pos = 0;
250
251     // map '0xc3..' utf8 characters to Latin1
252     while ((string::npos != (pos = s.find('\xc3',pos)))&&
253            (pos+1 < s.size()))
254     {
255         char c='*';
256         unsigned char p = s[pos+1];
257         if ((p>=0x80)&&(p<0xc0))
258             c = 0x40 + p;
259         char v[2];
260         v[0]=c;
261         v[1]=0;
262         s.replace(pos, 2, v);
263         pos++;
264     }
265
266 #ifdef DEBUG_ENCODING
267     printf("'%s': ", s.c_str());
268     for (pos = 0;pos<s.size();pos++)
269         printf("%02x ", (unsigned char) s[pos]);
270     printf("\n");
271 #endif
272
273     // hack: also map some Latin2 characters to plain-text ASCII
274     pos = 0;
275     while ((string::npos != (pos = s.find('\xc5',pos)))&&
276            (pos+1 < s.size()))
277     {
278         char c='*';
279         unsigned char p = s[pos+1];
280         switch(p)
281         {
282             case 0x82:c='l';break;
283             case 0x9a:c='S';break;
284             case 0x9b:c='s';break;
285             case 0xba:c='z';break;
286             case 0xbc:c='z';break;
287         }
288         char v[2];
289         v[0]=c;
290         v[1]=0;
291         s.replace(pos, 2, v);
292         pos++;
293     }
294 }