Clean up gettext initialization
[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 #include "log.h"
27
28 #if USE_GETTEXT && defined(_MSC_VER)
29 #include <windows.h>
30 #include <map>
31 #include <direct.h>
32 #include "filesys.h"
33
34 #define setlocale(category, localename) \
35         setlocale(category, MSVC_LocaleLookup(localename))
36
37 static std::map<std::wstring, std::wstring> glb_supported_locales;
38
39 /******************************************************************************/
40 BOOL CALLBACK UpdateLocaleCallback(LPTSTR pStr)
41 {
42         char* endptr = 0;
43         int LOCALEID = strtol(pStr, &endptr,16);
44
45         wchar_t buffer[LOCALE_NAME_MAX_LENGTH];
46         memset(buffer, 0, sizeof(buffer));
47         if (GetLocaleInfoW(
48                 LOCALEID,
49                 LOCALE_SISO639LANGNAME,
50                 buffer,
51                 LOCALE_NAME_MAX_LENGTH)) {
52
53                 std::wstring name = buffer;
54
55                 memset(buffer, 0, sizeof(buffer));
56                 GetLocaleInfoW(
57                 LOCALEID,
58                 LOCALE_SISO3166CTRYNAME,
59                 buffer,
60                 LOCALE_NAME_MAX_LENGTH);
61
62                 std::wstring country = buffer;
63
64                 memset(buffer, 0, sizeof(buffer));
65                 GetLocaleInfoW(
66                 LOCALEID,
67                 LOCALE_SENGLISHLANGUAGENAME,
68                 buffer,
69                 LOCALE_NAME_MAX_LENGTH);
70
71                 std::wstring languagename = buffer;
72
73                 /* set both short and long variant */
74                 glb_supported_locales[name] = languagename;
75                 glb_supported_locales[name + L"_" + country] = languagename;
76         }
77         return true;
78 }
79
80 /******************************************************************************/
81 const char* MSVC_LocaleLookup(const char* raw_shortname) {
82
83         /* NULL is used to read locale only so we need to return it too */
84         if (raw_shortname == NULL) return NULL;
85
86         std::string shortname(raw_shortname);
87         if (shortname == "C") return "C";
88         if (shortname == "") return "";
89
90         static std::string last_raw_value = "";
91         static std::string last_full_name = "";
92         static bool first_use = true;
93
94         if (last_raw_value == shortname) {
95                 return last_full_name.c_str();
96         }
97
98         if (first_use) {
99                 EnumSystemLocalesA(UpdateLocaleCallback, LCID_SUPPORTED | LCID_ALTERNATE_SORTS);
100                 first_use = false;
101         }
102
103         last_raw_value = shortname;
104
105         if (glb_supported_locales.find(utf8_to_wide(shortname)) != glb_supported_locales.end()) {
106                 last_full_name = wide_to_utf8(
107                         glb_supported_locales[utf8_to_wide(shortname)]);
108                 return last_full_name.c_str();
109         }
110
111         /* empty string is system default */
112         errorstream << "MSVC_LocaleLookup: unsupported locale: \"" << shortname
113                                 << "\" switching to system default!" << std::endl;
114         return "";
115 }
116
117 #endif
118
119 /******************************************************************************/
120 #ifdef _MSC_VER
121 void init_gettext(const char *path, const std::string &configured_language,
122         int argc, const char *argv[])
123 #else
124 void init_gettext(const char *path, const std::string &configured_language)
125 #endif
126 {
127 #if USE_GETTEXT
128         // First, try to set user override environment
129         if (!configured_language.empty()) {
130 #ifndef _WIN32
131                 // Add user specified locale to environment
132                 setenv("LANGUAGE", configured_language.c_str(), 1);
133
134                 // Reload locale with changed environment
135                 setlocale(LC_ALL, "");
136 #elif defined(_MSC_VER)
137                 std::string current_language;
138                 const char *env_lang = getenv("LANGUAGE");
139                 if (env_lang)
140                         current_language = env_lang;
141
142                 _putenv(("LANGUAGE=" + configured_language).c_str());
143                 SetEnvironmentVariableA("LANGUAGE", configured_language.c_str());
144
145 #ifndef SERVER
146                 // Hack to force gettext to see the right environment
147                 if (current_language != configured_language) {
148                         errorstream << "MSVC localization workaround active.  "
149                                 "Restarting " PROJECT_NAME_C " in a new environment!" << std::endl;
150
151                         std::string parameters;
152
153                         for (unsigned int i = 1; i < argc; i++) {
154                                 if (!parameters.empty())
155                                         parameters += ' ';
156
157                                 parameters += argv[i];
158                         }
159
160                         const char *ptr_parameters = NULL;
161
162                         if (!parameters.empty())
163                                 ptr_parameters = parameters.c_str();
164
165                         // Allow calling without an extension
166                         std::string app_name = argv[0];
167                         if (app_name.compare(appname.size() - 4, 4, ".exe") != 0)
168                                 app_name += ".exe";
169
170                         STARTUPINFO startup_info = {0};
171                         PROCESS_INFORMATION process_info = {0};
172
173                         bool success = CreateProcess(app_name.c_str(), (char *)ptr_parameters,
174                                 NULL, NULL, false, DETACHED_PROCESS | CREATE_UNICODE_ENVIRONMENT,
175                                 NULL, NULL, &startup_info, &process_info);
176
177                         if (success) {
178                                 exit(0);
179                                 // NOTREACHED
180                         } else {
181                                 char buffer[1024];
182
183                                 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
184                                         MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), buffer,
185                                         sizeof(buffer) - 1, NULL);
186
187                                 errorstream << "*******************************************************" << std::endl;
188                                 errorstream << "CMD: " << app_name << std::endl;
189                                 errorstream << "Failed to restart with current locale: " << std::endl;
190                                 errorstream << buffer;
191                                 errorstream << "Expect language to be broken!" << std::endl;
192                                 errorstream << "*******************************************************" << std::endl;
193                         }
194                 }
195 #else
196                 errorstream << "*******************************************************" << std::endl;
197                 errorstream << "Can't apply locale workaround for server!" << std::endl;
198                 errorstream << "Expect language to be broken!" << std::endl;
199                 errorstream << "*******************************************************" << std::endl;
200 #endif
201
202                 setlocale(LC_ALL, configured_language.c_str());
203 #else // Mingw
204                 _putenv(("LANGUAGE=" + configured_language).c_str());
205                 setlocale(LC_ALL, "");
206 #endif // ifndef _WIN32
207         }
208         else {
209                  /* set current system default locale */
210                 setlocale(LC_ALL, "");
211         }
212
213 #if defined(_WIN32)
214         if (getenv("LANGUAGE") != 0) {
215                 setlocale(LC_ALL, getenv("LANGUAGE"));
216         }
217 #ifdef _MSC_VER
218         else if (getenv("LANG") != 0) {
219                 setlocale(LC_ALL, getenv("LANG"));
220         }
221 #endif
222 #endif
223
224         static std::string name = lowercase(PROJECT_NAME);
225         bindtextdomain(name.c_str(), path);
226         textdomain(name.c_str());
227
228 #if defined(_WIN32)
229         // Set character encoding for Win32
230         char *tdomain = textdomain( (char *) NULL );
231         if( tdomain == NULL )
232         {
233                 errorstream << "Warning: domainname parameter is the null pointer" <<
234                                 ", default domain is not set" << std::endl;
235                 tdomain = (char *) "messages";
236         }
237         /* char *codeset = */bind_textdomain_codeset( tdomain, "UTF-8" );
238         //errorstream << "Gettext debug: domainname = " << tdomain << "; codeset = "<< codeset << std::endl;
239 #endif // defined(_WIN32)
240
241 #else
242         /* set current system default locale */
243         setlocale(LC_ALL, "");
244 #endif // if USE_GETTEXT
245
246         /* no matter what locale is used we need number format to be "C" */
247         /* to ensure formspec parameters are evaluated correct!          */
248
249         setlocale(LC_NUMERIC, "C");
250         infostream << "Message locale is now set to: "
251                         << setlocale(LC_ALL, 0) << std::endl;
252 }
253