|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #ifdef MOZ_WIDGET_QT |
|
7 #include <QString> |
|
8 #include <QtCore/QLocale> |
|
9 #endif |
|
10 |
|
11 #include "nsCOMPtr.h" |
|
12 #include "nsAutoPtr.h" |
|
13 #include "nsILocale.h" |
|
14 #include "nsILocaleService.h" |
|
15 #include "nsLocale.h" |
|
16 #include "nsCRT.h" |
|
17 #include "prprf.h" |
|
18 #include "nsTArray.h" |
|
19 #include "nsString.h" |
|
20 |
|
21 #include <ctype.h> |
|
22 |
|
23 #if defined(XP_WIN) |
|
24 # include "nsWin32Locale.h" |
|
25 #elif defined(XP_MACOSX) |
|
26 # include <Carbon/Carbon.h> |
|
27 #elif defined(XP_UNIX) |
|
28 # include <locale.h> |
|
29 # include <stdlib.h> |
|
30 # include "nsPosixLocale.h" |
|
31 #endif |
|
32 |
|
33 // |
|
34 // implementation constants |
|
35 const int LocaleListLength = 6; |
|
36 const char* LocaleList[LocaleListLength] = |
|
37 { |
|
38 NSILOCALE_COLLATE, |
|
39 NSILOCALE_CTYPE, |
|
40 NSILOCALE_MONETARY, |
|
41 NSILOCALE_NUMERIC, |
|
42 NSILOCALE_TIME, |
|
43 NSILOCALE_MESSAGE |
|
44 }; |
|
45 |
|
46 #define NSILOCALE_MAX_ACCEPT_LANGUAGE 16 |
|
47 #define NSILOCALE_MAX_ACCEPT_LENGTH 18 |
|
48 |
|
49 #if (defined(XP_UNIX) && !defined(XP_MACOSX)) |
|
50 static int posix_locale_category[LocaleListLength] = |
|
51 { |
|
52 LC_COLLATE, |
|
53 LC_CTYPE, |
|
54 LC_MONETARY, |
|
55 LC_NUMERIC, |
|
56 LC_TIME, |
|
57 #ifdef HAVE_I18N_LC_MESSAGES |
|
58 LC_MESSAGES |
|
59 #else |
|
60 LC_CTYPE |
|
61 #endif |
|
62 }; |
|
63 #endif |
|
64 |
|
65 // |
|
66 // nsILocaleService implementation |
|
67 // |
|
68 class nsLocaleService: public nsILocaleService { |
|
69 |
|
70 public: |
|
71 |
|
72 // |
|
73 // nsISupports |
|
74 // |
|
75 NS_DECL_THREADSAFE_ISUPPORTS |
|
76 |
|
77 // |
|
78 // nsILocaleService |
|
79 // |
|
80 NS_DECL_NSILOCALESERVICE |
|
81 |
|
82 |
|
83 nsLocaleService(void); |
|
84 virtual ~nsLocaleService(void); |
|
85 |
|
86 protected: |
|
87 |
|
88 nsresult SetSystemLocale(void); |
|
89 nsresult SetApplicationLocale(void); |
|
90 |
|
91 nsCOMPtr<nsILocale> mSystemLocale; |
|
92 nsCOMPtr<nsILocale> mApplicationLocale; |
|
93 |
|
94 }; |
|
95 |
|
96 // |
|
97 // nsLocaleService methods |
|
98 // |
|
99 nsLocaleService::nsLocaleService(void) |
|
100 { |
|
101 #ifdef XP_WIN |
|
102 nsAutoString xpLocale; |
|
103 // |
|
104 // get the system LCID |
|
105 // |
|
106 LCID win_lcid = GetSystemDefaultLCID(); |
|
107 NS_ENSURE_TRUE_VOID(win_lcid); |
|
108 nsWin32Locale::GetXPLocale(win_lcid, xpLocale); |
|
109 nsresult rv = NewLocale(xpLocale, getter_AddRefs(mSystemLocale)); |
|
110 NS_ENSURE_SUCCESS_VOID(rv); |
|
111 |
|
112 // |
|
113 // get the application LCID |
|
114 // |
|
115 win_lcid = GetUserDefaultLCID(); |
|
116 NS_ENSURE_TRUE_VOID(win_lcid); |
|
117 nsWin32Locale::GetXPLocale(win_lcid, xpLocale); |
|
118 rv = NewLocale(xpLocale, getter_AddRefs(mApplicationLocale)); |
|
119 NS_ENSURE_SUCCESS_VOID(rv); |
|
120 #endif |
|
121 #if defined(XP_UNIX) && !defined(XP_MACOSX) |
|
122 nsRefPtr<nsLocale> resultLocale(new nsLocale()); |
|
123 NS_ENSURE_TRUE_VOID(resultLocale); |
|
124 |
|
125 #ifdef MOZ_WIDGET_QT |
|
126 const char* lang = QLocale::system().name().toUtf8(); |
|
127 #else |
|
128 // Get system configuration |
|
129 const char* lang = getenv("LANG"); |
|
130 #endif |
|
131 |
|
132 nsAutoString xpLocale, platformLocale; |
|
133 nsAutoString category, category_platform; |
|
134 int i; |
|
135 |
|
136 for( i = 0; i < LocaleListLength; i++ ) { |
|
137 nsresult result; |
|
138 // setlocale( , "") evaluates LC_* and LANG |
|
139 char* lc_temp = setlocale(posix_locale_category[i], ""); |
|
140 CopyASCIItoUTF16(LocaleList[i], category); |
|
141 category_platform = category; |
|
142 category_platform.AppendLiteral("##PLATFORM"); |
|
143 if (lc_temp != nullptr) { |
|
144 result = nsPosixLocale::GetXPLocale(lc_temp, xpLocale); |
|
145 CopyASCIItoUTF16(lc_temp, platformLocale); |
|
146 } else { |
|
147 if ( lang == nullptr ) { |
|
148 platformLocale.AssignLiteral("en_US"); |
|
149 result = nsPosixLocale::GetXPLocale("en-US", xpLocale); |
|
150 } else { |
|
151 CopyASCIItoUTF16(lang, platformLocale); |
|
152 result = nsPosixLocale::GetXPLocale(lang, xpLocale); |
|
153 } |
|
154 } |
|
155 if (NS_FAILED(result)) { |
|
156 return; |
|
157 } |
|
158 resultLocale->AddCategory(category, xpLocale); |
|
159 resultLocale->AddCategory(category_platform, platformLocale); |
|
160 } |
|
161 mSystemLocale = do_QueryInterface(resultLocale); |
|
162 mApplicationLocale = do_QueryInterface(resultLocale); |
|
163 |
|
164 #endif // XP_UNIX |
|
165 |
|
166 #ifdef XP_MACOSX |
|
167 // Get string representation of user's current locale |
|
168 CFLocaleRef userLocaleRef = ::CFLocaleCopyCurrent(); |
|
169 CFStringRef userLocaleStr = ::CFLocaleGetIdentifier(userLocaleRef); |
|
170 ::CFRetain(userLocaleStr); |
|
171 |
|
172 nsAutoTArray<UniChar, 32> buffer; |
|
173 int size = ::CFStringGetLength(userLocaleStr); |
|
174 buffer.SetLength(size + 1); |
|
175 CFRange range = ::CFRangeMake(0, size); |
|
176 ::CFStringGetCharacters(userLocaleStr, range, buffer.Elements()); |
|
177 buffer[size] = 0; |
|
178 |
|
179 // Convert the locale string to the format that Mozilla expects |
|
180 nsAutoString xpLocale(reinterpret_cast<char16_t*>(buffer.Elements())); |
|
181 xpLocale.ReplaceChar('_', '-'); |
|
182 |
|
183 nsresult rv = NewLocale(xpLocale, getter_AddRefs(mSystemLocale)); |
|
184 if (NS_SUCCEEDED(rv)) { |
|
185 mApplicationLocale = mSystemLocale; |
|
186 } |
|
187 |
|
188 ::CFRelease(userLocaleStr); |
|
189 ::CFRelease(userLocaleRef); |
|
190 |
|
191 NS_ASSERTION(mApplicationLocale, "Failed to create locale objects"); |
|
192 #endif // XP_MACOSX |
|
193 } |
|
194 |
|
195 nsLocaleService::~nsLocaleService(void) |
|
196 { |
|
197 } |
|
198 |
|
199 NS_IMPL_ISUPPORTS(nsLocaleService, nsILocaleService) |
|
200 |
|
201 NS_IMETHODIMP |
|
202 nsLocaleService::NewLocale(const nsAString &aLocale, nsILocale **_retval) |
|
203 { |
|
204 nsresult result; |
|
205 |
|
206 *_retval = nullptr; |
|
207 |
|
208 nsRefPtr<nsLocale> resultLocale(new nsLocale()); |
|
209 if (!resultLocale) return NS_ERROR_OUT_OF_MEMORY; |
|
210 |
|
211 for (int32_t i = 0; i < LocaleListLength; i++) { |
|
212 NS_ConvertASCIItoUTF16 category(LocaleList[i]); |
|
213 result = resultLocale->AddCategory(category, aLocale); |
|
214 if (NS_FAILED(result)) return result; |
|
215 #if defined(XP_UNIX) && !defined(XP_MACOSX) |
|
216 category.AppendLiteral("##PLATFORM"); |
|
217 result = resultLocale->AddCategory(category, aLocale); |
|
218 if (NS_FAILED(result)) return result; |
|
219 #endif |
|
220 } |
|
221 |
|
222 NS_ADDREF(*_retval = resultLocale); |
|
223 return NS_OK; |
|
224 } |
|
225 |
|
226 |
|
227 NS_IMETHODIMP |
|
228 nsLocaleService::GetSystemLocale(nsILocale **_retval) |
|
229 { |
|
230 if (mSystemLocale) { |
|
231 NS_ADDREF(*_retval = mSystemLocale); |
|
232 return NS_OK; |
|
233 } |
|
234 |
|
235 *_retval = (nsILocale*)nullptr; |
|
236 return NS_ERROR_FAILURE; |
|
237 } |
|
238 |
|
239 NS_IMETHODIMP |
|
240 nsLocaleService::GetApplicationLocale(nsILocale **_retval) |
|
241 { |
|
242 if (mApplicationLocale) { |
|
243 NS_ADDREF(*_retval = mApplicationLocale); |
|
244 return NS_OK; |
|
245 } |
|
246 |
|
247 *_retval=(nsILocale*)nullptr; |
|
248 return NS_ERROR_FAILURE; |
|
249 } |
|
250 |
|
251 NS_IMETHODIMP |
|
252 nsLocaleService::GetLocaleFromAcceptLanguage(const char *acceptLanguage, nsILocale **_retval) |
|
253 { |
|
254 char* cPtr; |
|
255 char* cPtr1; |
|
256 char* cPtr2; |
|
257 int i; |
|
258 int j; |
|
259 int countLang = 0; |
|
260 char acceptLanguageList[NSILOCALE_MAX_ACCEPT_LANGUAGE][NSILOCALE_MAX_ACCEPT_LENGTH]; |
|
261 nsresult result; |
|
262 |
|
263 nsAutoArrayPtr<char> input(new char[strlen(acceptLanguage)+1]); |
|
264 |
|
265 strcpy(input, acceptLanguage); |
|
266 cPtr1 = input-1; |
|
267 cPtr2 = input; |
|
268 |
|
269 /* put in standard form */ |
|
270 while (*(++cPtr1)) { |
|
271 if (isalpha(*cPtr1)) *cPtr2++ = tolower(*cPtr1); /* force lower case */ |
|
272 else if (isspace(*cPtr1)) ; /* ignore any space */ |
|
273 else if (*cPtr1=='-') *cPtr2++ = '_'; /* "-" -> "_" */ |
|
274 else if (*cPtr1=='*') ; /* ignore "*" */ |
|
275 else *cPtr2++ = *cPtr1; /* else unchanged */ |
|
276 } |
|
277 *cPtr2 = '\0'; |
|
278 |
|
279 countLang = 0; |
|
280 |
|
281 if (strchr(input,';')) { |
|
282 /* deal with the quality values */ |
|
283 |
|
284 float qvalue[NSILOCALE_MAX_ACCEPT_LANGUAGE]; |
|
285 float qSwap; |
|
286 float bias = 0.0f; |
|
287 char* ptrLanguage[NSILOCALE_MAX_ACCEPT_LANGUAGE]; |
|
288 char* ptrSwap; |
|
289 |
|
290 cPtr = nsCRT::strtok(input,",",&cPtr2); |
|
291 while (cPtr) { |
|
292 qvalue[countLang] = 1.0f; |
|
293 /* add extra parens to get rid of warning */ |
|
294 if ((cPtr1 = strchr(cPtr,';')) != nullptr) { |
|
295 PR_sscanf(cPtr1,";q=%f",&qvalue[countLang]); |
|
296 *cPtr1 = '\0'; |
|
297 } |
|
298 if (strlen(cPtr)<NSILOCALE_MAX_ACCEPT_LANGUAGE) { /* ignore if too long */ |
|
299 qvalue[countLang] -= (bias += 0.0001f); /* to insure original order */ |
|
300 ptrLanguage[countLang++] = cPtr; |
|
301 if (countLang>=NSILOCALE_MAX_ACCEPT_LANGUAGE) break; /* quit if too many */ |
|
302 } |
|
303 cPtr = nsCRT::strtok(cPtr2,",",&cPtr2); |
|
304 } |
|
305 |
|
306 /* sort according to decending qvalue */ |
|
307 /* not a very good algorithm, but count is not likely large */ |
|
308 for ( i=0 ; i<countLang-1 ; i++ ) { |
|
309 for ( j=i+1 ; j<countLang ; j++ ) { |
|
310 if (qvalue[i]<qvalue[j]) { |
|
311 qSwap = qvalue[i]; |
|
312 qvalue[i] = qvalue[j]; |
|
313 qvalue[j] = qSwap; |
|
314 ptrSwap = ptrLanguage[i]; |
|
315 ptrLanguage[i] = ptrLanguage[j]; |
|
316 ptrLanguage[j] = ptrSwap; |
|
317 } |
|
318 } |
|
319 } |
|
320 for ( i=0 ; i<countLang ; i++ ) { |
|
321 PL_strncpyz(acceptLanguageList[i],ptrLanguage[i],NSILOCALE_MAX_ACCEPT_LENGTH); |
|
322 } |
|
323 |
|
324 } else { |
|
325 /* simple case: no quality values */ |
|
326 |
|
327 cPtr = nsCRT::strtok(input,",",&cPtr2); |
|
328 while (cPtr) { |
|
329 if (strlen(cPtr)<NSILOCALE_MAX_ACCEPT_LENGTH) { /* ignore if too long */ |
|
330 PL_strncpyz(acceptLanguageList[countLang++],cPtr,NSILOCALE_MAX_ACCEPT_LENGTH); |
|
331 if (countLang>=NSILOCALE_MAX_ACCEPT_LENGTH) break; /* quit if too many */ |
|
332 } |
|
333 cPtr = nsCRT::strtok(cPtr2,",",&cPtr2); |
|
334 } |
|
335 } |
|
336 |
|
337 // |
|
338 // now create the locale |
|
339 // |
|
340 result = NS_ERROR_FAILURE; |
|
341 if (countLang>0) { |
|
342 result = NewLocale(NS_ConvertASCIItoUTF16(acceptLanguageList[0]), _retval); |
|
343 } |
|
344 |
|
345 // |
|
346 // clean up |
|
347 // |
|
348 return result; |
|
349 } |
|
350 |
|
351 |
|
352 nsresult |
|
353 nsLocaleService::GetLocaleComponentForUserAgent(nsAString& retval) |
|
354 { |
|
355 nsCOMPtr<nsILocale> system_locale; |
|
356 nsresult result; |
|
357 |
|
358 result = GetSystemLocale(getter_AddRefs(system_locale)); |
|
359 if (NS_SUCCEEDED(result)) |
|
360 { |
|
361 result = system_locale-> |
|
362 GetCategory(NS_LITERAL_STRING(NSILOCALE_MESSAGE), retval); |
|
363 return result; |
|
364 } |
|
365 |
|
366 return result; |
|
367 } |
|
368 |
|
369 |
|
370 |
|
371 nsresult |
|
372 NS_NewLocaleService(nsILocaleService** result) |
|
373 { |
|
374 if(!result) |
|
375 return NS_ERROR_NULL_POINTER; |
|
376 *result = new nsLocaleService(); |
|
377 if (! *result) |
|
378 return NS_ERROR_OUT_OF_MEMORY; |
|
379 NS_ADDREF(*result); |
|
380 return NS_OK; |
|
381 } |