Set locale properly when built without gettext support
[oweals/minetest.git] / src / gettext.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <string>
21 #include <string.h>
22 #include <iostream>
23 #include <stdlib.h>
24 #include "gettext.h"
25 #include "util/string.h"
26
27 #if USE_GETTEXT and defined(_MSC_VER)
28 #include <WinNls.h>
29 #include <map>
30 #include <direct.h>
31 #include "filesys.h"
32
33 #define setlocale(category,localename) \
34         setlocale(category,MSVC_LocaleLookup(localename))
35
36 static std::map<std::wstring,std::wstring> glb_supported_locales;
37
38 /******************************************************************************/
39 BOOL CALLBACK UpdateLocaleCallback(LPTSTR pStr)
40 {
41         char* endptr = 0;
42         int LOCALEID = strtol(pStr,&endptr,16);
43
44         wchar_t buffer[LOCALE_NAME_MAX_LENGTH];
45         memset(buffer,0,sizeof(buffer));
46         if (GetLocaleInfoW(
47                 LOCALEID,
48                 LOCALE_SISO639LANGNAME,
49                 buffer,
50                 LOCALE_NAME_MAX_LENGTH)) {
51
52                 std::wstring name = buffer;
53
54                 memset(buffer,0,sizeof(buffer));
55                 GetLocaleInfoW(
56                 LOCALEID,
57                 LOCALE_SISO3166CTRYNAME,
58                 buffer,
59                 LOCALE_NAME_MAX_LENGTH);
60
61                 std::wstring country = buffer;
62
63                 memset(buffer,0,sizeof(buffer));
64                 GetLocaleInfoW(
65                 LOCALEID,
66                 LOCALE_SENGLISHLANGUAGENAME,
67                 buffer,
68                 LOCALE_NAME_MAX_LENGTH);
69
70                 std::wstring languagename = buffer;
71
72                 /* set both short and long variant */
73                 glb_supported_locales[name] = languagename;
74                 glb_supported_locales[name + L"_" + country] = languagename;
75         }
76         return true;
77 }
78
79 /******************************************************************************/
80 const char* MSVC_LocaleLookup(const char* raw_shortname) {
81
82         /* NULL is used to read locale only so we need to return it too */
83         if (raw_shortname == NULL) return NULL;
84
85         std::string shortname(raw_shortname);
86         if (shortname == "C") return "C";
87         if (shortname == "") return "";
88
89         static std::string last_raw_value = "";
90         static std::string last_full_name = "";
91         static bool first_use = true;
92
93         if (last_raw_value == shortname) {
94                 return last_full_name.c_str();
95         }
96
97         if (first_use) {
98                 EnumSystemLocalesA(UpdateLocaleCallback,LCID_SUPPORTED | LCID_ALTERNATE_SORTS);
99                 first_use = false;
100         }
101
102         last_raw_value = shortname;
103
104         if (glb_supported_locales.find(narrow_to_wide(shortname)) != glb_supported_locales.end()) {
105                 last_full_name = wide_to_narrow(glb_supported_locales[narrow_to_wide(shortname)]);
106                 return last_full_name.c_str();
107         }
108
109         /* empty string is system default */
110         errorstream << "MSVC_LocaleLookup: unsupported locale: \"" << shortname
111                                 << "\" switching to system default!" << std::endl;
112         return "";
113 }
114
115 #endif
116
117 /******************************************************************************/
118 #ifdef _MSC_VER
119 void init_gettext(const char *path,std::string configured_language,int argc, char** argv) {
120 #else
121 void init_gettext(const char *path,std::string configured_language) {
122 #endif
123 #if USE_GETTEXT
124         /** first try to set user override environment **/
125         if (configured_language.length() != 0) {
126 #ifndef _WIN32
127                 /* add user specified locale to environment */
128                 setenv("LANGUAGE", configured_language.c_str(), 1);
129
130                 /* reload locale with changed environment */
131                 setlocale(LC_ALL, "");
132 #elif defined(_MSC_VER)
133                 std::string current_language_var("");
134                 if (getenv("LANGUAGE") != 0) {
135                         current_language_var = std::string(getenv("LANGUAGE"));
136                 }
137
138                 char *lang_str = (char*)calloc(10 + configured_language.length(), sizeof(char));
139                 strcat(lang_str, "LANGUAGE=");
140                 strcat(lang_str, configured_language.c_str());
141                 putenv(lang_str);
142
143                 SetEnvironmentVariableA("LANGUAGE",configured_language.c_str());
144
145 #ifndef SERVER
146                 //very very dirty workaround to force gettext to see the right environment
147                 if (current_language_var != configured_language) {
148                         STARTUPINFO startupinfo;
149                         PROCESS_INFORMATION processinfo;
150                         memset(&startupinfo,0,sizeof(startupinfo));
151                         memset(&processinfo,0,sizeof(processinfo));
152                         errorstream << "MSVC localization workaround active restating minetest in new environment!" << std::endl;
153
154                         std::string parameters = "";
155
156                         for (unsigned int i=1;i < argc; i++) {
157                                 if (parameters != "") {
158                                         parameters += " ";
159                                 }
160                                 parameters += argv[i];
161                         }
162
163                         const char* ptr_parameters = 0;
164
165                         if (parameters != "") {
166                                 ptr_parameters = parameters.c_str();
167                         }
168                         
169                         /** users may start by short name in commandline without extention **/
170                         std::string appname = argv[0];
171                         if (appname.substr(appname.length() -4) != ".exe") {
172                                 appname += ".exe";
173                         }
174
175                         if (!CreateProcess(appname.c_str(),
176                                                                 (char*) ptr_parameters,
177                                                                 NULL,
178                                                                 NULL,
179                                                                 false,
180                                                                 DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT,
181                                                                 NULL,
182                                                                 NULL,
183                                                                 &startupinfo,
184                                                                 &processinfo)) {
185                                 char buffer[1024];              
186                                 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
187                                         NULL,
188                                         GetLastError(),
189                                         MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
190                                         buffer,
191                                         sizeof(buffer)-1,
192                                         NULL);
193                                 errorstream << "*******************************************************" << std::endl;
194                                 errorstream << "CMD: " << appname << std::endl;
195                                 errorstream << "Failed to restart with current locale: " << std::endl;
196                                 errorstream << buffer;
197                                 errorstream << "Expect language to be broken!" << std::endl;
198                                 errorstream << "*******************************************************" << std::endl;
199                         }
200                         else {
201                                 exit(0);
202                         }
203 #else
204                         errorstream << "*******************************************************" << std::endl;
205                         errorstream << "Can't apply locale workaround for server!" << std::encl;
206                         errorstream << "Expect language to be broken!" << std::endl;
207                         errorstream << "*******************************************************" << std::endl;
208
209 #endif
210                 }
211
212                 setlocale(LC_ALL,configured_language.c_str());
213 #else // Mingw
214                 char *lang_str = (char*)calloc(10 + configured_language.length(), sizeof(char));
215                 strcat(lang_str, "LANGUAGE=");
216                 strcat(lang_str, configured_language.c_str());
217                 putenv(lang_str);
218
219                 setlocale(LC_ALL, "");
220 #endif // ifndef _WIN32
221         }
222         else {
223                  /* set current system default locale */
224                 setlocale(LC_ALL, "");
225         }
226
227 #if defined(_WIN32)
228         if (getenv("LANGUAGE") != 0) {
229                 setlocale(LC_ALL, getenv("LANGUAGE"));
230         }
231 #ifdef _MSC_VER
232         else if (getenv("LANG") != 0) {
233                 setlocale(LC_ALL, getenv("LANG"));
234         }
235 #endif
236 #endif
237
238         bindtextdomain(PROJECT_NAME, path);
239         textdomain(PROJECT_NAME);
240
241 #if defined(_WIN32)
242         // Set character encoding for Win32
243         char *tdomain = textdomain( (char *) NULL );
244         if( tdomain == NULL )
245         {
246                 errorstream << "Warning: domainname parameter is the null pointer" <<
247                                 ", default domain is not set" << std::endl;
248                 tdomain = (char *) "messages";
249         }
250         /* char *codeset = */bind_textdomain_codeset( tdomain, "UTF-8" );
251         //errorstream << "Gettext debug: domainname = " << tdomain << "; codeset = "<< codeset << std::endl;
252 #endif // defined(_WIN32)
253
254 #else
255         /* set current system default locale */
256         setlocale(LC_ALL, "");
257 #endif // if USE_GETTEXT
258
259         /* no matter what locale is used we need number format to be "C" */
260         /* to ensure formspec parameters are evaluated correct!          */
261
262         setlocale(LC_NUMERIC,"C");
263         infostream << "Message locale is now set to: "
264                         << setlocale(LC_ALL,0) << std::endl;
265 }
266
267
268
269