]> git.saurik.com Git - wxWidgets.git/blame - src/common/intl.cpp
Supress SetFocus() warning in wxVListBoxComboPopup
[wxWidgets.git] / src / common / intl.cpp
CommitLineData
c801d85f 1/////////////////////////////////////////////////////////////////////////////
dccce9ea 2// Name: src/common/intl.cpp
77ffb593 3// Purpose: Internationalization and localisation for wxWidgets
c801d85f 4// Author: Vadim Zeitlin
849a28d0
VS
5// Modified by: Michael N. Filippov <michael@idisys.iae.nsk.su>
6// (2003/09/30 - PluralForms support)
c801d85f
KB
7// Created: 29/01/98
8// RCS-ID: $Id$
9// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
65571936 10// Licence: wxWindows licence
c801d85f
KB
11/////////////////////////////////////////////////////////////////////////////
12
13// ============================================================================
1678ad78 14// declaration
c801d85f
KB
15// ============================================================================
16
17// ----------------------------------------------------------------------------
18// headers
19// ----------------------------------------------------------------------------
20
c801d85f
KB
21// For compilers that support precompilation, includes "wx.h".
22#include "wx/wxprec.h"
23
24#ifdef __BORLANDC__
84c18814 25 #pragma hdrstop
c801d85f
KB
26#endif
27
6908078e
VZ
28#ifdef __EMX__
29// The following define is needed by Innotek's libc to
30// make the definition of struct localeconv available.
31#define __INTERNAL_DEFS
32#endif
33
d427503c
VZ
34#if wxUSE_INTL
35
ad9835c9
WS
36#ifndef WX_PRECOMP
37 #include "wx/dynarray.h"
38 #include "wx/string.h"
39 #include "wx/intl.h"
40 #include "wx/log.h"
ad9835c9 41 #include "wx/utils.h"
670f9935 42 #include "wx/app.h"
df69528b 43 #include "wx/hashmap.h"
02761f6c 44 #include "wx/module.h"
ad9835c9 45#endif // WX_PRECOMP
1c193821
JS
46
47#ifndef __WXWINCE__
ad9835c9 48 #include <locale.h>
1c193821
JS
49#endif
50
ad9835c9 51// standard headers
dccce9ea
VZ
52#include <ctype.h>
53#include <stdlib.h>
2ec6905c 54#ifdef HAVE_LANGINFO_H
670f9935 55 #include <langinfo.h>
2ec6905c 56#endif
7502ba29 57
1c193821
JS
58#ifdef __WIN32__
59 #include "wx/msw/private.h"
1c193821
JS
60#endif
61
c801d85f 62#include "wx/file.h"
58ad1bab 63#include "wx/filename.h"
dccce9ea 64#include "wx/tokenzr.h"
030c0bea 65#include "wx/fontmap.h"
664e1314 66#include "wx/scopedptr.h"
4d931bcc 67#include "wx/apptrait.h"
f830b2b1 68#include "wx/stdpaths.h"
a64be16e 69#include "wx/hashset.h"
dccce9ea 70
89a7e1ff 71#if defined(__WXOSX__)
c933e267 72 #include "wx/osx/core/cfref.h"
529e125d 73 #include <CoreFoundation/CFLocale.h>
e9e53eb1 74 #include <CoreFoundation/CFDateFormatter.h>
c933e267 75 #include "wx/osx/core/cfstring.h"
5a9cd2d1 76#endif
dff6cf57 77
c801d85f
KB
78// ----------------------------------------------------------------------------
79// constants
80// ----------------------------------------------------------------------------
81
a0516656 82#define TRACE_I18N wxS("i18n")
ca6a9a14 83
ea144923
VS
84// ============================================================================
85// implementation
86// ============================================================================
563d535e 87
849a28d0 88// ----------------------------------------------------------------------------
ea144923 89// global functions
18e065b4
VS
90// ----------------------------------------------------------------------------
91
ea144923
VS
92static wxLocale *wxSetLocale(wxLocale *pLocale);
93
18e065b4
VS
94namespace
95{
96
ce411ca8 97// get just the language part ("en" in "en_GB")
ea144923 98inline wxString ExtractLang(const wxString& langFull)
18e065b4 99{
ce411ca8 100 return langFull.BeforeFirst('_');
18e065b4
VS
101}
102
ea144923
VS
103// helper functions of GetSystemLanguage()
104#ifdef __UNIX__
18e065b4 105
ea144923
VS
106// get everything else (including the leading '_')
107inline wxString ExtractNotLang(const wxString& langFull)
108{
ce411ca8
VS
109 size_t pos = langFull.find('_');
110 if ( pos != wxString::npos )
111 return langFull.substr(pos);
112 else
113 return wxString();
ea144923 114}
18e065b4 115
ea144923 116#endif // __UNIX__
18e065b4 117
ea144923 118} // anonymous namespace
18e065b4 119
ea144923
VS
120// ----------------------------------------------------------------------------
121// wxLanguageInfo
122// ----------------------------------------------------------------------------
18e065b4 123
ea144923 124#ifdef __WXMSW__
18e065b4 125
ea144923
VS
126// helper used by wxLanguageInfo::GetLocaleName() and elsewhere to determine
127// whether the locale is Unicode-only (it is if this function returns empty
128// string)
129static wxString wxGetANSICodePageForLocale(LCID lcid)
130{
131 wxString cp;
18e065b4 132
ea144923
VS
133 wxChar buffer[16];
134 if ( ::GetLocaleInfo(lcid, LOCALE_IDEFAULTANSICODEPAGE,
135 buffer, WXSIZEOF(buffer)) > 0 )
18e065b4 136 {
ea144923
VS
137 if ( buffer[0] != wxT('0') || buffer[1] != wxT('\0') )
138 cp = buffer;
139 //else: this locale doesn't use ANSI code page
18e065b4
VS
140 }
141
ea144923 142 return cp;
18e065b4
VS
143}
144
ea144923 145wxUint32 wxLanguageInfo::GetLCID() const
18e065b4 146{
ea144923 147 return MAKELCID(MAKELANGID(WinLang, WinSublang), SORT_DEFAULT);
18e065b4
VS
148}
149
ea144923 150wxString wxLanguageInfo::GetLocaleName() const
18e065b4 151{
ea144923 152 wxString locale;
18e065b4 153
ea144923 154 const LCID lcid = GetLCID();
18e065b4 155
ea144923
VS
156 wxChar buffer[256];
157 buffer[0] = wxT('\0');
158 if ( !::GetLocaleInfo(lcid, LOCALE_SENGLANGUAGE, buffer, WXSIZEOF(buffer)) )
849a28d0 159 {
ea144923
VS
160 wxLogLastError(wxT("GetLocaleInfo(LOCALE_SENGLANGUAGE)"));
161 return locale;
849a28d0 162 }
18e065b4 163
ea144923
VS
164 locale << buffer;
165 if ( ::GetLocaleInfo(lcid, LOCALE_SENGCOUNTRY,
166 buffer, WXSIZEOF(buffer)) > 0 )
849a28d0 167 {
ea144923 168 locale << wxT('_') << buffer;
849a28d0
VS
169 }
170
ea144923
VS
171 const wxString cp = wxGetANSICodePageForLocale(lcid);
172 if ( !cp.empty() )
563d535e 173 {
ea144923 174 locale << wxT('.') << cp;
563d535e 175 }
18e065b4 176
ea144923 177 return locale;
563d535e 178}
afc94fa6 179
ea144923 180#endif // __WXMSW__
18e065b4 181
c801d85f
KB
182// ----------------------------------------------------------------------------
183// wxLocale
184// ----------------------------------------------------------------------------
185
d3f3e35f
VS
186#include "wx/arrimpl.cpp"
187WX_DECLARE_EXPORTED_OBJARRAY(wxLanguageInfo, wxLanguageInfoArray);
4115960d 188WX_DEFINE_OBJARRAY(wxLanguageInfoArray)
d3f3e35f 189
41780009
VS
190wxLanguageInfoArray *wxLocale::ms_languagesDB = NULL;
191
192/*static*/ void wxLocale::CreateLanguagesDB()
193{
194 if (ms_languagesDB == NULL)
195 {
196 ms_languagesDB = new wxLanguageInfoArray;
197 InitLanguagesDB();
198 }
199}
200
201/*static*/ void wxLocale::DestroyLanguagesDB()
202{
203 delete ms_languagesDB;
204 ms_languagesDB = NULL;
205}
206
207
ea8f6fc7 208void wxLocale::DoCommonInit()
c801d85f 209{
ca642c8a 210 m_pszOldLocale = NULL;
eb48778d 211
ca642c8a 212 m_pOldLocale = wxSetLocale(this);
0d28a1f9
VS
213
214 // Set translations object, but only if the user didn't do so yet.
215 // This is to preserve compatibility with wx-2.8 where wxLocale was
216 // the only API for translations. wxLocale works as a stack, with
217 // latest-created one being the active one:
218 // wxLocale loc_fr(wxLANGUAGE_FRENCH);
219 // // _() returns French
220 // {
221 // wxLocale loc_cs(wxLANGUAGE_CZECH);
222 // // _() returns Czech
223 // }
224 // // _() returns French again
225 wxTranslations *oldTrans = wxTranslations::Get();
226 if ( !oldTrans ||
227 (m_pOldLocale && oldTrans == &m_pOldLocale->m_translations) )
228 {
229 wxTranslations::SetNonOwned(&m_translations);
230 }
eb48778d 231
ca642c8a
FM
232 m_language = wxLANGUAGE_UNKNOWN;
233 m_initialized = false;
23fcecf7
VZ
234}
235
236// NB: this function has (desired) side effect of changing current locale
31b7522e
VS
237bool wxLocale::Init(const wxString& name,
238 const wxString& shortName,
239 const wxString& locale,
3acf8a8d
VS
240 bool bLoadDefault
241#if WXWIN_COMPATIBILITY_2_8
242 ,bool bConvertEncoding
243#endif
244 )
23fcecf7 245{
3acf8a8d
VS
246#if WXWIN_COMPATIBILITY_2_8
247 wxASSERT_MSG( bConvertEncoding,
248 wxS("wxLocale::Init with bConvertEncoding=false is no longer supported, add charset to your catalogs") );
249#endif
250
18e065b4
VS
251 bool ret = DoInit(name, shortName, locale);
252
253 // NB: don't use 'lang' here, 'language' may be wxLANGUAGE_DEFAULT
0d28a1f9
VS
254 wxTranslations *t = wxTranslations::Get();
255 if ( t )
256 {
257 t->SetLanguage(shortName);
18e065b4 258
0d28a1f9
VS
259 if ( bLoadDefault )
260 t->AddStdCatalog();
261 }
18e065b4
VS
262
263 return ret;
264}
265
266bool wxLocale::DoInit(const wxString& name,
267 const wxString& shortName,
268 const wxString& locale)
269{
270 wxASSERT_MSG( !m_initialized,
271 wxS("you can't call wxLocale::Init more than once") );
272
ca642c8a
FM
273 m_initialized = true;
274 m_strLocale = name;
275 m_strShort = shortName;
ca642c8a 276 m_language = wxLANGUAGE_UNKNOWN;
23fcecf7 277
ca642c8a
FM
278 // change current locale (default: same as long name)
279 wxString szLocale(locale);
280 if ( szLocale.empty() )
281 {
282 // the argument to setlocale()
283 szLocale = shortName;
52558258 284
ca642c8a
FM
285 wxCHECK_MSG( !szLocale.empty(), false,
286 wxS("no locale to set in wxLocale::Init()") );
287 }
10545ca4 288
ca642c8a
FM
289 const char *oldLocale = wxSetlocale(LC_ALL, szLocale);
290 if ( oldLocale )
291 m_pszOldLocale = wxStrdup(oldLocale);
292 else
293 m_pszOldLocale = NULL;
1c193821 294
ca642c8a 295 if ( m_pszOldLocale == NULL )
af588446 296 {
ca642c8a 297 wxLogError(_("locale '%s' can not be set."), szLocale);
af588446 298 }
c801d85f 299
ca642c8a
FM
300 // the short name will be used to look for catalog files as well,
301 // so we need something here
302 if ( m_strShort.empty() ) {
303 // FIXME I don't know how these 2 letter abbreviations are formed,
304 // this wild guess is surely wrong
305 if ( !szLocale.empty() )
306 {
307 m_strShort += (wxChar)wxTolower(szLocale[0]);
308 if ( szLocale.length() > 1 )
309 m_strShort += (wxChar)wxTolower(szLocale[1]);
310 }
70426fba 311 }
7af89395 312
18e065b4 313 return true;
c801d85f
KB
314}
315
41524ffc 316
1cc549e5 317#if defined(__UNIX__) && wxUSE_UNICODE && !defined(__WXMAC__)
52de37c7 318static const char *wxSetlocaleTryUTF8(int c, const wxString& lc)
41524ffc 319{
aff909dd 320 const char *l = NULL;
cb352236
VS
321
322 // NB: We prefer to set UTF-8 locale if it's possible and only fall back to
323 // non-UTF-8 locale if it fails
324
52de37c7 325 if ( !lc.empty() )
41524ffc 326 {
7beb59f3 327 wxString buf(lc);
2d34a303 328 wxString buf2;
a0516656 329 buf2 = buf + wxS(".UTF-8");
52de37c7 330 l = wxSetlocale(c, buf2);
2d34a303
VS
331 if ( !l )
332 {
a0516656 333 buf2 = buf + wxS(".utf-8");
52de37c7 334 l = wxSetlocale(c, buf2);
2d34a303
VS
335 }
336 if ( !l )
337 {
a0516656 338 buf2 = buf + wxS(".UTF8");
52de37c7 339 l = wxSetlocale(c, buf2);
2d34a303
VS
340 }
341 if ( !l )
342 {
a0516656 343 buf2 = buf + wxS(".utf8");
52de37c7 344 l = wxSetlocale(c, buf2);
2d34a303 345 }
41524ffc 346 }
cb352236
VS
347
348 // if we can't set UTF-8 locale, try non-UTF-8 one:
349 if ( !l )
350 l = wxSetlocale(c, lc);
351
41524ffc
VS
352 return l;
353}
354#else
cb352236 355#define wxSetlocaleTryUTF8(c, lc) wxSetlocale(c, lc)
41524ffc
VS
356#endif
357
d3f3e35f
VS
358bool wxLocale::Init(int language, int flags)
359{
3acf8a8d
VS
360#if WXWIN_COMPATIBILITY_2_8
361 wxASSERT_MSG( !(flags & wxLOCALE_CONV_ENCODING),
362 wxS("wxLOCALE_CONV_ENCODING is no longer supported, add charset to your catalogs") );
363#endif
364
2aebd278
VZ
365 bool ret = true;
366
d3f3e35f 367 int lang = language;
ec37df57
VZ
368 if (lang == wxLANGUAGE_DEFAULT)
369 {
370 // auto detect the language
371 lang = GetSystemLanguage();
372 }
373
374 // We failed to detect system language, so we will use English:
375 if (lang == wxLANGUAGE_UNKNOWN)
376 {
ca642c8a 377 return false;
ec37df57
VZ
378 }
379
14f8fa9d 380 const wxLanguageInfo *info = GetLanguageInfo(lang);
d3f3e35f 381
d3f3e35f
VS
382 // Unknown language:
383 if (info == NULL)
384 {
a0516656 385 wxLogError(wxS("Unknown language %i."), lang);
7beb59f3 386 return false;
d3f3e35f
VS
387 }
388
389 wxString name = info->Description;
390 wxString canonical = info->CanonicalName;
391 wxString locale;
ec37df57 392
d3f3e35f 393 // Set the locale:
55034339 394#if defined(__OS2__)
52de37c7 395 const char *retloc = wxSetlocale(LC_ALL , wxEmptyString);
55034339 396#elif defined(__UNIX__) && !defined(__WXMAC__)
d368dbea 397 if (language != wxLANGUAGE_DEFAULT)
ec37df57 398 locale = info->CanonicalName;
d3f3e35f 399
52de37c7 400 const char *retloc = wxSetlocaleTryUTF8(LC_ALL, locale);
d3f3e35f 401
f6ba36e4 402 const wxString langOnly = ExtractLang(locale);
2b5f62a0 403 if ( !retloc )
d3f3e35f
VS
404 {
405 // Some C libraries don't like xx_YY form and require xx only
cb352236 406 retloc = wxSetlocaleTryUTF8(LC_ALL, langOnly);
d3f3e35f 407 }
d368dbea
VZ
408
409#if wxUSE_FONTMAP
410 // some systems (e.g. FreeBSD and HP-UX) don't have xx_YY aliases but
411 // require the full xx_YY.encoding form, so try using UTF-8 because this is
412 // the only thing we can do generically
413 //
414 // TODO: add encodings applicable to each language to the lang DB and try
415 // them all in turn here
2b5f62a0 416 if ( !retloc )
d3f3e35f 417 {
d368dbea
VZ
418 const wxChar **names =
419 wxFontMapperBase::GetAllEncodingNames(wxFONTENCODING_UTF8);
420 while ( *names )
421 {
a0516656 422 retloc = wxSetlocale(LC_ALL, locale + wxS('.') + *names++);
d368dbea
VZ
423 if ( retloc )
424 break;
425 }
d3f3e35f 426 }
d368dbea
VZ
427#endif // wxUSE_FONTMAP
428
2b5f62a0 429 if ( !retloc )
d3f3e35f 430 {
d368dbea
VZ
431 // Some C libraries (namely glibc) still use old ISO 639,
432 // so will translate the abbrev for them
433 wxString localeAlt;
a0516656 434 if ( langOnly == wxS("he") )
f6ba36e4 435 localeAlt = wxS("iw") + ExtractNotLang(locale);
a0516656 436 else if ( langOnly == wxS("id") )
f6ba36e4 437 localeAlt = wxS("in") + ExtractNotLang(locale);
a0516656 438 else if ( langOnly == wxS("yi") )
f6ba36e4 439 localeAlt = wxS("ji") + ExtractNotLang(locale);
a0516656
VZ
440 else if ( langOnly == wxS("nb") )
441 localeAlt = wxS("no_NO");
442 else if ( langOnly == wxS("nn") )
443 localeAlt = wxS("no_NY");
d368dbea
VZ
444
445 if ( !localeAlt.empty() )
446 {
cb352236 447 retloc = wxSetlocaleTryUTF8(LC_ALL, localeAlt);
d368dbea 448 if ( !retloc )
f6ba36e4 449 retloc = wxSetlocaleTryUTF8(LC_ALL, ExtractLang(localeAlt));
d368dbea 450 }
d3f3e35f 451 }
d368dbea 452
2b5f62a0 453 if ( !retloc )
2aebd278 454 ret = false;
d368dbea
VZ
455
456#ifdef __AIX__
31b7522e
VS
457 // at least in AIX 5.2 libc is buggy and the string returned from
458 // setlocale(LC_ALL) can't be passed back to it because it returns 6
459 // strings (one for each locale category), i.e. for C locale we get back
460 // "C C C C C C"
d368dbea 461 //
31b7522e
VS
462 // this contradicts IBM own docs but this is not of much help, so just work
463 // around it in the crudest possible manner
c222ad41 464 char* p = const_cast<char*>(wxStrchr(retloc, ' '));
d368dbea 465 if ( p )
52de37c7 466 *p = '\0';
d368dbea
VZ
467#endif // __AIX__
468
d3f3e35f 469#elif defined(__WIN32__)
52de37c7 470 const char *retloc = "C";
42db82fd 471 if ( language != wxLANGUAGE_DEFAULT )
d3f3e35f 472 {
42db82fd 473 if ( info->WinLang == 0 )
d3f3e35f 474 {
a0516656 475 wxLogWarning(wxS("Locale '%s' not supported by OS."), name.c_str());
2b5f62a0 476 // retloc already set to "C"
63986ba6 477 }
42db82fd 478 else // language supported by Windows
ec37df57 479 {
99abd9c8
VZ
480 // Windows CE doesn't have SetThreadLocale() and there doesn't seem
481 // to be any equivalent
1c193821 482#ifndef __WXWINCE__
fb090fa1
VZ
483 const wxUint32 lcid = info->GetLCID();
484
86d62b51
VZ
485 // change locale used by Windows functions
486 ::SetThreadLocale(lcid);
1c193821 487#endif
99abd9c8 488
86d62b51
VZ
489 // and also call setlocale() to change locale used by the CRT
490 locale = info->GetLocaleName();
491 if ( locale.empty() )
65dc921d 492 {
2aebd278 493 ret = false;
2b5f62a0 494 }
86d62b51 495 else // have a valid locale
c611452f 496 {
65dc921d 497 retloc = wxSetlocale(LC_ALL, locale);
c611452f 498 }
d3f3e35f
VS
499 }
500 }
42db82fd 501 else // language == wxLANGUAGE_DEFAULT
2b5f62a0 502 {
c611452f 503 retloc = wxSetlocale(LC_ALL, wxEmptyString);
42db82fd 504 }
99abd9c8 505
42db82fd
VZ
506#if wxUSE_UNICODE && (defined(__VISUALC__) || defined(__MINGW32__))
507 // VC++ setlocale() (also used by Mingw) can't set locale to languages that
508 // can only be written using Unicode, therefore wxSetlocale() call fails
509 // for such languages but we don't want to report it as an error -- so that
510 // at least message catalogs can be used.
511 if ( !retloc )
512 {
513 if ( wxGetANSICodePageForLocale(LOCALE_USER_DEFAULT).empty() )
65dc921d 514 {
42db82fd
VZ
515 // we set the locale to a Unicode-only language, don't treat the
516 // inability of CRT to use it as an error
517 retloc = "C";
65dc921d 518 }
2b5f62a0 519 }
42db82fd 520#endif // CRT not handling Unicode-only languages
c611452f 521
2b5f62a0 522 if ( !retloc )
2aebd278 523 ret = false;
85fb2206 524#elif defined(__WXMAC__)
b51daa4f 525 if (lang == wxLANGUAGE_DEFAULT)
85fb2206
SC
526 locale = wxEmptyString;
527 else
528 locale = info->CanonicalName;
529
52de37c7 530 const char *retloc = wxSetlocale(LC_ALL, locale);
85fb2206
SC
531
532 if ( !retloc )
533 {
534 // Some C libraries don't like xx_YY form and require xx only
f6ba36e4 535 retloc = wxSetlocale(LC_ALL, ExtractLang(locale));
85fb2206 536 }
d3f3e35f 537#else
ead190e4 538 wxUnusedVar(flags);
7beb59f3 539 return false;
e879e020 540 #define WX_NO_LOCALE_SUPPORT
d3f3e35f 541#endif
ec37df57 542
e879e020 543#ifndef WX_NO_LOCALE_SUPPORT
2aebd278
VZ
544 if ( !ret )
545 {
546 wxLogWarning(_("Cannot set locale to language \"%s\"."), name.c_str());
547
548 // continue nevertheless and try to load at least the translations for
549 // this language
550 }
551
18e065b4 552 if ( !DoInit(name, canonical, retloc) )
2aebd278
VZ
553 {
554 ret = false;
555 }
10545ca4 556
549a7c37 557 if (IsOk()) // setlocale() succeeded
10545ca4
VZ
558 m_language = lang;
559
18e065b4 560 // NB: don't use 'lang' here, 'language'
0d28a1f9
VS
561 wxTranslations *t = wxTranslations::Get();
562 if ( t )
563 {
564 t->SetLanguage(static_cast<wxLanguage>(language));
d3f3e35f 565
0d28a1f9
VS
566 if ( flags & wxLOCALE_LOAD_DEFAULT )
567 t->AddStdCatalog();
568 }
d3f3e35f 569
18e065b4
VS
570 return ret;
571#endif // !WX_NO_LOCALE_SUPPORT
fd323a5e
VZ
572}
573
41780009 574/*static*/ int wxLocale::GetSystemLanguage()
d3f3e35f 575{
41780009 576 CreateLanguagesDB();
ec37df57
VZ
577
578 // init i to avoid compiler warning
579 size_t i = 0,
ca642c8a 580 count = ms_languagesDB->GetCount();
d3f3e35f 581
9b3dbb11 582#if defined(__UNIX__)
ec37df57
VZ
583 // first get the string identifying the language from the environment
584 wxString langFull;
9b3dbb11 585#ifdef __WXMAC__
9b3dbb11 586 wxCFRef<CFLocaleRef> userLocaleRef(CFLocaleCopyCurrent());
529e125d
JS
587
588 // because the locale identifier (kCFLocaleIdentifier) is formatted a little bit differently, eg
9b3dbb11 589 // az_Cyrl_AZ@calendar=buddhist;currency=JPY we just recreate the base info as expected by wx here
529e125d 590
80539f04 591 wxCFStringRef str(wxCFRetain((CFStringRef)CFLocaleGetValue(userLocaleRef, kCFLocaleLanguageCode)));
9b3dbb11 592 langFull = str.AsString()+"_";
80539f04 593 str.reset(wxCFRetain((CFStringRef)CFLocaleGetValue(userLocaleRef, kCFLocaleCountryCode)));
9b3dbb11
SC
594 langFull += str.AsString();
595#else
a0516656
VZ
596 if (!wxGetEnv(wxS("LC_ALL"), &langFull) &&
597 !wxGetEnv(wxS("LC_MESSAGES"), &langFull) &&
598 !wxGetEnv(wxS("LANG"), &langFull))
ec37df57 599 {
ff1bd293
VZ
600 // no language specified, treat it as English
601 return wxLANGUAGE_ENGLISH_US;
ec37df57 602 }
d3f3e35f 603
a0516656 604 if ( langFull == wxS("C") || langFull == wxS("POSIX") )
d3f3e35f 605 {
ff1bd293
VZ
606 // default C locale is English too
607 return wxLANGUAGE_ENGLISH_US;
d3f3e35f 608 }
9b3dbb11 609#endif
d3f3e35f 610
ec37df57
VZ
611 // the language string has the following form
612 //
efd1393b 613 // lang[_LANG][.encoding][@modifier]
ec37df57 614 //
efd1393b
VZ
615 // (see environ(5) in the Open Unix specification)
616 //
617 // where lang is the primary language, LANG is a sublang/territory,
618 // encoding is the charset to use and modifier "allows the user to select
619 // a specific instance of localization data within a single category"
ec37df57
VZ
620 //
621 // for example, the following strings are valid:
622 // fr
623 // fr_FR
624 // de_DE.iso88591
efd1393b
VZ
625 // de_DE@euro
626 // de_DE.iso88591@euro
ec37df57
VZ
627
628 // for now we don't use the encoding, although we probably should (doing
629 // translations of the msg catalogs on the fly as required) (TODO)
efd1393b 630 //
8cee59b5
VZ
631 // we need the modified for languages like Valencian: ca_ES@valencia
632 // though, remember it
633 wxString modifier;
634 size_t posModifier = langFull.find_first_of(wxS("@"));
635 if ( posModifier != wxString::npos )
636 modifier = langFull.Mid(posModifier);
637
a0516656 638 size_t posEndLang = langFull.find_first_of(wxS("@."));
efd1393b
VZ
639 if ( posEndLang != wxString::npos )
640 {
641 langFull.Truncate(posEndLang);
642 }
ec37df57 643
ec37df57 644 // do we have just the language (or sublang too)?
ce411ca8
VS
645 const bool justLang = langFull.find('_') == wxString::npos;
646
647 // 0. Make sure the lang is according to latest ISO 639
648 // (this is necessary because glibc uses iw and in instead
649 // of he and id respectively).
650
651 // the language itself (second part is the dialect/sublang)
652 wxString langOrig = ExtractLang(langFull);
653
654 wxString lang;
655 if ( langOrig == wxS("iw"))
656 lang = wxS("he");
657 else if (langOrig == wxS("in"))
658 lang = wxS("id");
659 else if (langOrig == wxS("ji"))
660 lang = wxS("yi");
661 else if (langOrig == wxS("no_NO"))
662 lang = wxS("nb_NO");
663 else if (langOrig == wxS("no_NY"))
664 lang = wxS("nn_NO");
665 else if (langOrig == wxS("no"))
666 lang = wxS("nb_NO");
667 else
668 lang = langOrig;
ec37df57 669
ce411ca8
VS
670 // did we change it?
671 if ( lang != langOrig )
672 {
673 langFull = lang + ExtractNotLang(langFull);
674 }
675
676 // 1. Try to find the language either as is:
677 // a) With modifier if set
678 if ( !modifier.empty() )
679 {
680 wxString langFullWithModifier = langFull + modifier;
681 for ( i = 0; i < count; i++ )
ec37df57 682 {
ce411ca8
VS
683 if ( ms_languagesDB->Item(i).CanonicalName == langFullWithModifier )
684 break;
8cee59b5 685 }
ce411ca8 686 }
8cee59b5 687
ce411ca8
VS
688 // b) Without modifier
689 if ( modifier.empty() || i == count )
690 {
691 for ( i = 0; i < count; i++ )
8cee59b5 692 {
ce411ca8
VS
693 if ( ms_languagesDB->Item(i).CanonicalName == langFull )
694 break;
d3f3e35f 695 }
ce411ca8 696 }
d3f3e35f 697
ce411ca8
VS
698 // 2. If langFull is of the form xx_YY, try to find xx:
699 if ( i == count && !justLang )
700 {
701 for ( i = 0; i < count; i++ )
d3f3e35f 702 {
ce411ca8 703 if ( ms_languagesDB->Item(i).CanonicalName == lang )
d3f3e35f 704 {
ce411ca8 705 break;
d3f3e35f
VS
706 }
707 }
ce411ca8 708 }
d3f3e35f 709
ce411ca8
VS
710 // 3. If langFull is of the form xx, try to find any xx_YY record:
711 if ( i == count && justLang )
712 {
713 for ( i = 0; i < count; i++ )
d3f3e35f 714 {
ce411ca8
VS
715 if ( ExtractLang(ms_languagesDB->Item(i).CanonicalName)
716 == langFull )
d3f3e35f 717 {
ce411ca8 718 break;
d3f3e35f
VS
719 }
720 }
721 }
ce411ca8
VS
722
723
724 if ( i == count )
d3f3e35f 725 {
ce411ca8
VS
726 // In addition to the format above, we also can have full language
727 // names in LANG env var - for example, SuSE is known to use
728 // LANG="german" - so check for use of non-standard format and try to
729 // find the name in verbose description.
ec37df57 730 for ( i = 0; i < count; i++ )
d3f3e35f 731 {
41780009 732 if (ms_languagesDB->Item(i).Description.CmpNoCase(langFull) == 0)
d3f3e35f 733 {
d3f3e35f
VS
734 break;
735 }
736 }
737 }
d3f3e35f
VS
738#elif defined(__WIN32__)
739 LCID lcid = GetUserDefaultLCID();
ec37df57 740 if ( lcid != 0 )
d3f3e35f 741 {
ec37df57
VZ
742 wxUint32 lang = PRIMARYLANGID(LANGIDFROMLCID(lcid));
743 wxUint32 sublang = SUBLANGID(LANGIDFROMLCID(lcid));
744
745 for ( i = 0; i < count; i++ )
d3f3e35f 746 {
41780009
VS
747 if (ms_languagesDB->Item(i).WinLang == lang &&
748 ms_languagesDB->Item(i).WinSublang == sublang)
ec37df57
VZ
749 {
750 break;
751 }
d3f3e35f
VS
752 }
753 }
ec37df57
VZ
754 //else: leave wxlang == wxLANGUAGE_UNKNOWN
755#endif // Unix/Win32
d3f3e35f 756
ec37df57
VZ
757 if ( i < count )
758 {
759 // we did find a matching entry, use it
41780009 760 return ms_languagesDB->Item(i).Language;
ec37df57 761 }
d3f3e35f 762
ec37df57
VZ
763 // no info about this language in the database
764 return wxLANGUAGE_UNKNOWN;
765}
d3f3e35f 766
dccce9ea
VZ
767// ----------------------------------------------------------------------------
768// encoding stuff
769// ----------------------------------------------------------------------------
770
771// this is a bit strange as under Windows we get the encoding name using its
772// numeric value and under Unix we do it the other way round, but this just
97626624 773// reflects the way different systems provide the encoding info
dccce9ea
VZ
774
775/* static */
776wxString wxLocale::GetSystemEncodingName()
777{
778 wxString encname;
779
c67d6888 780#if defined(__WIN32__) && !defined(__WXMICROWIN__)
dccce9ea
VZ
781 // FIXME: what is the error return value for GetACP()?
782 UINT codepage = ::GetACP();
a0516656 783 encname.Printf(wxS("windows-%u"), codepage);
1ad48afb
SC
784#elif defined(__WXMAC__)
785 // default is just empty string, this resolves to the default system
786 // encoding later
dccce9ea
VZ
787#elif defined(__UNIX_LIKE__)
788
789#if defined(HAVE_LANGINFO_H) && defined(CODESET)
790 // GNU libc provides current character set this way (this conforms
791 // to Unix98)
2ec6905c
VS
792 char *oldLocale = strdup(setlocale(LC_CTYPE, NULL));
793 setlocale(LC_CTYPE, "");
f284605f 794 const char *alang = nl_langinfo(CODESET);
2ec6905c 795 setlocale(LC_CTYPE, oldLocale);
030c0bea 796 free(oldLocale);
84f858e9
VZ
797
798 if ( alang )
dccce9ea 799 {
09d2016d 800 encname = wxString::FromAscii( alang );
dccce9ea 801 }
09d2016d 802 else // nl_langinfo() failed
dccce9ea
VZ
803#endif // HAVE_LANGINFO_H
804 {
805 // if we can't get at the character set directly, try to see if it's in
806 // the environment variables (in most cases this won't work, but I was
807 // out of ideas)
b1ac3b56 808 char *lang = getenv( "LC_ALL");
d3b9f782 809 char *dot = lang ? strchr(lang, '.') : NULL;
dccce9ea
VZ
810 if (!dot)
811 {
b1ac3b56 812 lang = getenv( "LC_CTYPE" );
dccce9ea 813 if ( lang )
b1ac3b56 814 dot = strchr(lang, '.' );
dccce9ea
VZ
815 }
816 if (!dot)
817 {
b1ac3b56 818 lang = getenv( "LANG");
dccce9ea 819 if ( lang )
b1ac3b56 820 dot = strchr(lang, '.');
dccce9ea
VZ
821 }
822
823 if ( dot )
824 {
b1ac3b56 825 encname = wxString::FromAscii( dot+1 );
dccce9ea
VZ
826 }
827 }
828#endif // Win32/Unix
829
830 return encname;
831}
832
833/* static */
834wxFontEncoding wxLocale::GetSystemEncoding()
835{
c67d6888 836#if defined(__WIN32__) && !defined(__WXMICROWIN__)
dccce9ea
VZ
837 UINT codepage = ::GetACP();
838
3c832d58 839 // wxWidgets only knows about CP1250-1257, 874, 932, 936, 949, 950
dccce9ea
VZ
840 if ( codepage >= 1250 && codepage <= 1257 )
841 {
842 return (wxFontEncoding)(wxFONTENCODING_CP1250 + codepage - 1250);
843 }
c7821f94 844
3c832d58
DS
845 if ( codepage == 874 )
846 {
847 return wxFONTENCODING_CP874;
848 }
849
c7821f94
VZ
850 if ( codepage == 932 )
851 {
852 return wxFONTENCODING_CP932;
853 }
854
855 if ( codepage == 936 )
856 {
857 return wxFONTENCODING_CP936;
858 }
859
860 if ( codepage == 949 )
861 {
862 return wxFONTENCODING_CP949;
863 }
864
865 if ( codepage == 950 )
866 {
867 return wxFONTENCODING_CP950;
868 }
ec79aded 869#elif defined(__WXMAC__)
73b51ea4 870 CFStringEncoding encoding = 0 ;
7beb59f3 871 encoding = CFStringGetSystemEncoding() ;
9e687455 872 return wxMacGetFontEncFromSystemEnc( encoding ) ;
1e6feb95 873#elif defined(__UNIX_LIKE__) && wxUSE_FONTMAP
09d2016d 874 const wxString encname = GetSystemEncodingName();
dccce9ea
VZ
875 if ( !encname.empty() )
876 {
09d2016d 877 wxFontEncoding enc = wxFontMapperBase::GetEncodingFromName(encname);
9cf28550 878
2b5f62a0 879 // on some modern Linux systems (RedHat 8) the default system locale
13f095d2 880 // is UTF8 -- but it isn't supported by wxGTK1 in ANSI build at all so
2b5f62a0 881 // don't even try to use it in this case
13f095d2
VZ
882#if !wxUSE_UNICODE && \
883 ((defined(__WXGTK__) && !defined(__WXGTK20__)) || defined(__WXMOTIF__))
2b5f62a0
VZ
884 if ( enc == wxFONTENCODING_UTF8 )
885 {
886 // the most similar supported encoding...
887 enc = wxFONTENCODING_ISO8859_1;
888 }
889#endif // !wxUSE_UNICODE
890
09d2016d
VZ
891 // GetEncodingFromName() returns wxFONTENCODING_DEFAULT for C locale
892 // (a.k.a. US-ASCII) which is arguably a bug but keep it like this for
893 // backwards compatibility and just take care to not return
894 // wxFONTENCODING_DEFAULT from here as this surely doesn't make sense
8b34b163
VZ
895 if ( enc == wxFONTENCODING_DEFAULT )
896 {
897 // we don't have wxFONTENCODING_ASCII, so use the closest one
898 return wxFONTENCODING_ISO8859_1;
899 }
900
901 if ( enc != wxFONTENCODING_MAX )
9cf28550
VZ
902 {
903 return enc;
904 }
905 //else: return wxFONTENCODING_SYSTEM below
dccce9ea
VZ
906 }
907#endif // Win32/Unix
908
909 return wxFONTENCODING_SYSTEM;
910}
911
14f8fa9d
VZ
912/* static */
913void wxLocale::AddLanguage(const wxLanguageInfo& info)
d3f3e35f 914{
41780009
VS
915 CreateLanguagesDB();
916 ms_languagesDB->Add(info);
d3f3e35f
VS
917}
918
14f8fa9d
VZ
919/* static */
920const wxLanguageInfo *wxLocale::GetLanguageInfo(int lang)
921{
922 CreateLanguagesDB();
923
f0ab09cc
VZ
924 // calling GetLanguageInfo(wxLANGUAGE_DEFAULT) is a natural thing to do, so
925 // make it work
926 if ( lang == wxLANGUAGE_DEFAULT )
927 lang = GetSystemLanguage();
928
9d1e1be4 929 const size_t count = ms_languagesDB->GetCount();
14f8fa9d
VZ
930 for ( size_t i = 0; i < count; i++ )
931 {
932 if ( ms_languagesDB->Item(i).Language == lang )
933 {
b2fde0cf
CE
934 // We need to create a temporary here in order to make this work with BCC in final build mode
935 wxLanguageInfo *ptr = &ms_languagesDB->Item(i);
936 return ptr;
14f8fa9d
VZ
937 }
938 }
939
940 return NULL;
941}
942
4a6e4a46
VS
943/* static */
944wxString wxLocale::GetLanguageName(int lang)
945{
18e065b4
VS
946 if ( lang == wxLANGUAGE_DEFAULT || lang == wxLANGUAGE_UNKNOWN )
947 return wxEmptyString;
948
4a6e4a46
VS
949 const wxLanguageInfo *info = GetLanguageInfo(lang);
950 if ( !info )
951 return wxEmptyString;
952 else
953 return info->Description;
954}
955
18e065b4
VS
956/* static */
957wxString wxLocale::GetLanguageCanonicalName(int lang)
958{
959 if ( lang == wxLANGUAGE_DEFAULT || lang == wxLANGUAGE_UNKNOWN )
960 return wxEmptyString;
961
962 const wxLanguageInfo *info = GetLanguageInfo(lang);
963 if ( !info )
964 return wxEmptyString;
965 else
966 return info->CanonicalName;
967}
968
9d1e1be4
VZ
969/* static */
970const wxLanguageInfo *wxLocale::FindLanguageInfo(const wxString& locale)
971{
972 CreateLanguagesDB();
973
974 const wxLanguageInfo *infoRet = NULL;
975
976 const size_t count = ms_languagesDB->GetCount();
977 for ( size_t i = 0; i < count; i++ )
978 {
979 const wxLanguageInfo *info = &ms_languagesDB->Item(i);
980
981 if ( wxStricmp(locale, info->CanonicalName) == 0 ||
982 wxStricmp(locale, info->Description) == 0 )
983 {
984 // exact match, stop searching
985 infoRet = info;
986 break;
987 }
988
a0516656 989 if ( wxStricmp(locale, info->CanonicalName.BeforeFirst(wxS('_'))) == 0 )
9d1e1be4
VZ
990 {
991 // a match -- but maybe we'll find an exact one later, so continue
992 // looking
993 //
994 // OTOH, maybe we had already found a language match and in this
52de37c7 995 // case don't overwrite it because the entry for the default
9d1e1be4
VZ
996 // country always appears first in ms_languagesDB
997 if ( !infoRet )
998 infoRet = info;
999 }
1000 }
1001
1002 return infoRet;
1003}
1004
d3f3e35f
VS
1005wxString wxLocale::GetSysName() const
1006{
1007 return wxSetlocale(LC_ALL, NULL);
1008}
1009
c801d85f
KB
1010// clean up
1011wxLocale::~wxLocale()
1012{
0d28a1f9
VS
1013 // Restore old translations object.
1014 // See DoCommonInit() for explanation of why this is needed for backward
1015 // compatibility.
18e065b4
VS
1016 if ( wxTranslations::Get() == &m_translations )
1017 {
1018 if ( m_pOldLocale )
1019 wxTranslations::SetNonOwned(&m_pOldLocale->m_translations);
1020 else
1021 wxTranslations::Set(NULL);
fd323a5e
VZ
1022 }
1023
eb48778d
VZ
1024 // restore old locale pointer
1025 wxSetLocale(m_pOldLocale);
bea561ce 1026
e36e6f95 1027 wxSetlocale(LC_ALL, m_pszOldLocale);
10545ca4 1028 free((wxChar *)m_pszOldLocale); // const_cast
c801d85f
KB
1029}
1030
c801d85f 1031
cec5ffc4
VZ
1032// check if the given locale is provided by OS and C run time
1033/* static */
1034bool wxLocale::IsAvailable(int lang)
1035{
1036 const wxLanguageInfo *info = wxLocale::GetLanguageInfo(lang);
a0516656 1037 wxCHECK_MSG( info, false, wxS("invalid language") );
cec5ffc4 1038
327bf990 1039#if defined(__WIN32__)
cec5ffc4
VZ
1040 if ( !info->WinLang )
1041 return false;
1042
ea65760d 1043 if ( !::IsValidLocale(info->GetLCID(), LCID_INSTALLED) )
327bf990
RD
1044 return false;
1045
1046#elif defined(__UNIX__)
52de37c7
VS
1047
1048 // Test if setting the locale works, then set it back.
375330ac
FM
1049 const char *oldLocale = wxSetlocaleTryUTF8(LC_ALL, info->CanonicalName);
1050 if ( !oldLocale )
327bf990
RD
1051 {
1052 // Some C libraries don't like xx_YY form and require xx only
375330ac
FM
1053 oldLocale = wxSetlocaleTryUTF8(LC_ALL, ExtractLang(info->CanonicalName));
1054 if ( !oldLocale )
327bf990
RD
1055 return false;
1056 }
1057 // restore the original locale
52de37c7
VS
1058 wxSetlocale(LC_ALL, oldLocale);
1059#endif
cec5ffc4
VZ
1060
1061 return true;
1062}
1063
0d28a1f9
VS
1064
1065bool wxLocale::AddCatalog(const wxString& domain)
1066{
1067 wxTranslations *t = wxTranslations::Get();
1068 if ( !t )
1069 return false;
1070 return t->AddCatalog(domain);
1071}
1072
1073bool wxLocale::AddCatalog(const wxString& domain, wxLanguage msgIdLanguage)
1074{
1075 wxTranslations *t = wxTranslations::Get();
1076 if ( !t )
1077 return false;
1078 return t->AddCatalog(domain, msgIdLanguage);
1079}
1080
d721baa9 1081// add a catalog to our linked list
31b7522e 1082bool wxLocale::AddCatalog(const wxString& szDomain,
ca642c8a
FM
1083 wxLanguage msgIdLanguage,
1084 const wxString& msgIdCharset)
c801d85f 1085{
0d28a1f9
VS
1086 wxTranslations *t = wxTranslations::Get();
1087 if ( !t )
1088 return false;
18e065b4
VS
1089#if wxUSE_UNICODE
1090 wxUnusedVar(msgIdCharset);
0d28a1f9 1091 return t->AddCatalog(szDomain, msgIdLanguage);
18e065b4 1092#else
0d28a1f9 1093 return t->AddCatalog(szDomain, msgIdLanguage, msgIdCharset);
18e065b4 1094#endif
c801d85f
KB
1095}
1096
0d28a1f9
VS
1097bool wxLocale::IsLoaded(const wxString& domain) const
1098{
1099 wxTranslations *t = wxTranslations::Get();
1100 if ( !t )
1101 return false;
1102 return t->IsLoaded(domain);
1103}
1104
1105wxString wxLocale::GetHeaderValue(const wxString& header,
1106 const wxString& domain) const
1107{
1108 wxTranslations *t = wxTranslations::Get();
1109 if ( !t )
1110 return wxEmptyString;
1111 return t->GetHeaderValue(header, domain);
1112}
1113
cfb20656
VZ
1114// ----------------------------------------------------------------------------
1115// accessors for locale-dependent data
1116// ----------------------------------------------------------------------------
1117
89a7e1ff
VZ
1118#if defined(__WXMSW__) || defined(__WXOSX__)
1119
1120namespace
1121{
1122
1123// This function translates from Unicode date formats described at
1124//
1125// http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
1126//
1127// to strftime()-like syntax. This translation is not lossless but we try to do
1128// our best.
1129
1130static wxString TranslateFromUnicodeFormat(const wxString& fmt)
1131{
1132 wxString fmtWX;
1133 fmtWX.reserve(fmt.length());
1134
1135 char chLast = '\0';
1136 size_t lastCount = 0;
03647350
VZ
1137
1138 const char* formatchars =
3e43b647
SC
1139 "dghHmMsSy"
1140#ifdef __WXMSW__
1141 "t"
47029843
SC
1142#else
1143 "EawD"
3e43b647
SC
1144#endif
1145 ;
89a7e1ff
VZ
1146 for ( wxString::const_iterator p = fmt.begin(); /* end handled inside */; ++p )
1147 {
1148 if ( p != fmt.end() )
1149 {
1150 if ( *p == chLast )
1151 {
1152 lastCount++;
1153 continue;
1154 }
1155
1156 const wxUniChar ch = (*p).GetValue();
3e43b647 1157 if ( ch.IsAscii() && strchr(formatchars, ch) )
89a7e1ff
VZ
1158 {
1159 // these characters come in groups, start counting them
1160 chLast = ch;
1161 lastCount = 1;
1162 continue;
1163 }
1164 }
1165
1166 // interpret any special characters we collected so far
1167 if ( lastCount )
1168 {
1169 switch ( chLast )
1170 {
1171 case 'd':
1172 switch ( lastCount )
1173 {
1174 case 1: // d
1175 case 2: // dd
1176 // these two are the same as we don't distinguish
1177 // between 1 and 2 digits for days
1178 fmtWX += "%d";
1179 break;
47029843 1180#ifdef __WXMSW__
89a7e1ff
VZ
1181 case 3: // ddd
1182 fmtWX += "%a";
1183 break;
1184
1185 case 4: // dddd
1186 fmtWX += "%A";
1187 break;
47029843 1188#endif
89a7e1ff
VZ
1189 default:
1190 wxFAIL_MSG( "too many 'd's" );
1191 }
1192 break;
47029843
SC
1193#ifndef __WXMSW__
1194 case 'D':
1195 switch ( lastCount )
1196 {
1197 case 1: // D
1198 case 2: // DD
1199 case 3: // DDD
1200 fmtWX += "%j";
1201 break;
1202
1203 default:
1204 wxFAIL_MSG( "wrong number of 'D's" );
1205 }
1206 break;
1207 case 'w':
1208 switch ( lastCount )
1209 {
1210 case 1: // w
1211 case 2: // ww
1212 fmtWX += "%W";
1213 break;
1214
1215 default:
1216 wxFAIL_MSG( "wrong number of 'w's" );
1217 }
1218 break;
1219 case 'E':
1220 switch ( lastCount )
1221 {
1222 case 1: // E
1223 case 2: // EE
1224 case 3: // EEE
1225 fmtWX += "%a";
1226 break;
1227 case 4: // EEEE
1228 fmtWX += "%A";
1229 break;
1230 case 5: // EEEEE
1231 fmtWX += "%a";
1232 break;
89a7e1ff 1233
47029843
SC
1234 default:
1235 wxFAIL_MSG( "wrong number of 'E's" );
1236 }
1237 break;
1238#endif
89a7e1ff
VZ
1239 case 'M':
1240 switch ( lastCount )
1241 {
1242 case 1: // M
1243 case 2: // MM
1244 // as for 'd' and 'dd' above
1245 fmtWX += "%m";
1246 break;
1247
1248 case 3:
1249 fmtWX += "%b";
1250 break;
1251
1252 case 4:
1253 fmtWX += "%B";
1254 break;
1255
1256 default:
1257 wxFAIL_MSG( "too many 'M's" );
1258 }
1259 break;
1260
1261 case 'y':
1262 switch ( lastCount )
1263 {
1264 case 1: // y
1265 case 2: // yy
1266 fmtWX += "%y";
1267 break;
1268
1269 case 4: // yyyy
1270 fmtWX += "%Y";
1271 break;
1272
1273 default:
1274 wxFAIL_MSG( "wrong number of 'y's" );
1275 }
1276 break;
1277
1278 case 'H':
1279 switch ( lastCount )
1280 {
1281 case 1: // H
1282 case 2: // HH
1283 fmtWX += "%H";
1284 break;
1285
1286 default:
1287 wxFAIL_MSG( "wrong number of 'H's" );
1288 }
1289 break;
1290
1291 case 'h':
1292 switch ( lastCount )
1293 {
1294 case 1: // h
1295 case 2: // hh
756b5a98 1296 fmtWX += "%I";
89a7e1ff
VZ
1297 break;
1298
1299 default:
1300 wxFAIL_MSG( "wrong number of 'h's" );
1301 }
1302 break;
1303
1304 case 'm':
1305 switch ( lastCount )
1306 {
1307 case 1: // m
1308 case 2: // mm
1309 fmtWX += "%M";
1310 break;
1311
1312 default:
1313 wxFAIL_MSG( "wrong number of 'm's" );
1314 }
1315 break;
1316
1317 case 's':
1318 switch ( lastCount )
1319 {
1320 case 1: // s
1321 case 2: // ss
1322 fmtWX += "%S";
1323 break;
1324
1325 default:
1326 wxFAIL_MSG( "wrong number of 's's" );
1327 }
1328 break;
1329
1330 case 'g':
1331 // strftime() doesn't have era string,
1332 // ignore this format
1333 wxASSERT_MSG( lastCount <= 2, "too many 'g's" );
03647350 1334
89a7e1ff 1335 break;
47029843 1336#ifndef __WXMSW__
3e43b647
SC
1337 case 'a':
1338 fmtWX += "%p";
1339 break;
47029843 1340#endif
3e43b647
SC
1341#ifdef __WXMSW__
1342 case 't':
1343 switch ( lastCount )
1344 {
1345 case 1: // t
1346 case 2: // tt
1347 fmtWX += "%p";
1348 break;
1349
1350 default:
1351 wxFAIL_MSG( "too many 't's" );
1352 }
1353 break;
1354#endif
89a7e1ff
VZ
1355 default:
1356 wxFAIL_MSG( "unreachable" );
1357 }
1358
1359 chLast = '\0';
1360 lastCount = 0;
1361 }
1362
1363 if ( p == fmt.end() )
1364 break;
1365
1366 // not a special character so must be just a separator, treat as is
9a83f860 1367 if ( *p == wxT('%') )
89a7e1ff
VZ
1368 {
1369 // this one needs to be escaped
9a83f860 1370 fmtWX += wxT('%');
89a7e1ff
VZ
1371 }
1372
1373 fmtWX += *p;
1374 }
1375
1376 return fmtWX;
1377}
1378
1379} // anonymous namespace
1380
1381#endif // __WXMSW__ || __WXOSX__
1382
dff6cf57 1383#if defined(__WXMSW__)
cfb20656 1384
89a7e1ff
VZ
1385namespace
1386{
1387
1388LCTYPE GetLCTYPEFormatFromLocalInfo(wxLocaleInfo index)
1389{
1390 switch ( index )
1391 {
1392 case wxLOCALE_SHORT_DATE_FMT:
1393 return LOCALE_SSHORTDATE;
1394
1395 case wxLOCALE_LONG_DATE_FMT:
1396 return LOCALE_SLONGDATE;
1397
1398 case wxLOCALE_TIME_FMT:
1399 return LOCALE_STIMEFORMAT;
1400
1401 default:
1402 wxFAIL_MSG( "no matching LCTYPE" );
1403 }
1404
1405 return 0;
1406}
1407
1408} // anonymous namespace
1409
cfb20656 1410/* static */
3fe73755 1411wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat))
cfb20656 1412{
529e125d 1413 wxUint32 lcid = LOCALE_USER_DEFAULT;
89a7e1ff 1414 if ( wxGetLocale() )
529e125d 1415 {
89a7e1ff
VZ
1416 const wxLanguageInfo * const
1417 info = GetLanguageInfo(wxGetLocale()->GetLanguage());
ea65760d
VZ
1418 if ( info )
1419 lcid = info->GetLCID();
529e125d
JS
1420 }
1421
cfb20656 1422 wxString str;
89a7e1ff
VZ
1423
1424 wxChar buf[256];
1425 buf[0] = wxT('\0');
1426
1427 switch ( index )
cfb20656 1428 {
60d876f3 1429 case wxLOCALE_DECIMAL_POINT:
89a7e1ff
VZ
1430 if ( ::GetLocaleInfo(lcid, LOCALE_SDECIMAL, buf, WXSIZEOF(buf)) )
1431 str = buf;
cfb20656 1432 break;
89a7e1ff
VZ
1433
1434 case wxLOCALE_SHORT_DATE_FMT:
1435 case wxLOCALE_LONG_DATE_FMT:
1436 case wxLOCALE_TIME_FMT:
1437 if ( ::GetLocaleInfo(lcid, GetLCTYPEFormatFromLocalInfo(index),
1438 buf, WXSIZEOF(buf)) )
1439 {
1440 return TranslateFromUnicodeFormat(buf);
1441 }
cfb20656 1442 break;
89a7e1ff
VZ
1443
1444 case wxLOCALE_DATE_TIME_FMT:
1445 // there doesn't seem to be any specific setting for this, so just
1446 // combine date and time ones
cd8a8b45
VZ
1447 //
1448 // we use the short date because this is what "%c" uses by default
1449 // ("%#c" uses long date but we have no way to specify the
1450 // alternate representation here)
89a7e1ff 1451 {
cd8a8b45 1452 const wxString datefmt = GetInfo(wxLOCALE_SHORT_DATE_FMT);
89a7e1ff
VZ
1453 if ( datefmt.empty() )
1454 break;
1455
1456 const wxString timefmt = GetInfo(wxLOCALE_TIME_FMT);
1457 if ( timefmt.empty() )
1458 break;
1459
1460 str << datefmt << ' ' << timefmt;
1461 }
cfb20656 1462 break;
89a7e1ff 1463
cfb20656 1464 default:
89a7e1ff 1465 wxFAIL_MSG( "unknown wxLocaleInfo" );
cfb20656 1466 }
89a7e1ff 1467
cfb20656
VZ
1468 return str;
1469}
1470
89a7e1ff 1471#elif defined(__WXOSX__)
dff6cf57
VZ
1472
1473/* static */
1474wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat))
1475{
82de050c
VZ
1476 CFLocaleRef userLocaleRefRaw;
1477 if ( wxGetLocale() )
1478 {
1479 userLocaleRefRaw = CFLocaleCreate
ca642c8a 1480 (
82de050c
VZ
1481 kCFAllocatorDefault,
1482 wxCFStringRef(wxGetLocale()->GetCanonicalName())
ca642c8a 1483 );
82de050c
VZ
1484 }
1485 else // no current locale, use the default one
1486 {
1487 userLocaleRefRaw = CFLocaleCopyCurrent();
1488 }
1489
1490 wxCFRef<CFLocaleRef> userLocaleRef(userLocaleRefRaw);
1491
73b51ea4 1492 CFStringRef cfstr = 0;
dff6cf57
VZ
1493 switch ( index )
1494 {
1495 case wxLOCALE_THOUSANDS_SEP:
73b51ea4 1496 cfstr = (CFStringRef) CFLocaleGetValue(userLocaleRef, kCFLocaleGroupingSeparator);
dff6cf57
VZ
1497 break;
1498
1499 case wxLOCALE_DECIMAL_POINT:
73b51ea4 1500 cfstr = (CFStringRef) CFLocaleGetValue(userLocaleRef, kCFLocaleDecimalSeparator);
dff6cf57
VZ
1501 break;
1502
89a7e1ff
VZ
1503 case wxLOCALE_SHORT_DATE_FMT:
1504 case wxLOCALE_LONG_DATE_FMT:
1505 case wxLOCALE_DATE_TIME_FMT:
1506 case wxLOCALE_TIME_FMT:
e9e53eb1
SC
1507 {
1508 CFDateFormatterStyle dateStyle = kCFDateFormatterNoStyle;
1509 CFDateFormatterStyle timeStyle = kCFDateFormatterNoStyle;
1510 switch (index )
1511 {
1512 case wxLOCALE_SHORT_DATE_FMT:
47029843 1513 dateStyle = kCFDateFormatterShortStyle;
e9e53eb1
SC
1514 break;
1515 case wxLOCALE_LONG_DATE_FMT:
9fbcc679 1516 dateStyle = kCFDateFormatterFullStyle;
e9e53eb1
SC
1517 break;
1518 case wxLOCALE_DATE_TIME_FMT:
47029843 1519 dateStyle = kCFDateFormatterFullStyle;
e9e53eb1
SC
1520 timeStyle = kCFDateFormatterMediumStyle;
1521 break;
1522 case wxLOCALE_TIME_FMT:
1523 timeStyle = kCFDateFormatterMediumStyle;
1524 break;
1525 default:
1526 wxFAIL_MSG( "unexpected time locale" );
1527 return wxString();
1528 }
1529 wxCFRef<CFDateFormatterRef> dateFormatter( CFDateFormatterCreate
1530 (NULL, userLocaleRef, dateStyle, timeStyle));
1531 wxCFStringRef cfs = wxCFRetain( CFDateFormatterGetFormat(dateFormatter ));
47029843
SC
1532 wxString format = TranslateFromUnicodeFormat(cfs.AsString());
1533 // we always want full years
1534 format.Replace("%y","%Y");
1535 return format;
e9e53eb1
SC
1536 }
1537 break;
89a7e1ff 1538
dff6cf57
VZ
1539 default:
1540 wxFAIL_MSG( "Unknown locale info" );
89a7e1ff 1541 return wxString();
dff6cf57
VZ
1542 }
1543
73b51ea4 1544 wxCFStringRef str(wxCFRetain(cfstr));
dff6cf57
VZ
1545 return str.AsString();
1546}
1547
89a7e1ff
VZ
1548#else // !__WXMSW__ && !__WXOSX__, assume generic POSIX
1549
1550namespace
1551{
1552
1553wxString GetDateFormatFromLangInfo(wxLocaleInfo index)
1554{
1555#ifdef HAVE_LANGINFO_H
1556 // array containing parameters for nl_langinfo() indexes by offset of index
1557 // from wxLOCALE_SHORT_DATE_FMT
1558 static const nl_item items[] =
1559 {
1560 D_FMT, D_T_FMT, D_T_FMT, T_FMT,
1561 };
1562
1563 const int nlidx = index - wxLOCALE_SHORT_DATE_FMT;
1564 if ( nlidx < 0 || nlidx >= (int)WXSIZEOF(items) )
1565 {
1566 wxFAIL_MSG( "logic error in GetInfo() code" );
1567 return wxString();
1568 }
1569
1570 const wxString fmt(nl_langinfo(items[nlidx]));
1571
1572 // just return the format returned by nl_langinfo() except for long date
1573 // format which we need to recover from date/time format ourselves (but not
1574 // if we failed completely)
1575 if ( fmt.empty() || index != wxLOCALE_LONG_DATE_FMT )
1576 return fmt;
1577
1578 // this is not 100% precise but the idea is that a typical date/time format
1579 // under POSIX systems is a combination of a long date format with time one
1580 // so we should be able to get just the long date format by removing all
1581 // time-specific format specifiers
1582 static const char *timeFmtSpecs = "HIklMpPrRsSTXzZ";
1583 static const char *timeSep = " :./-";
1584
1585 wxString fmtDateOnly;
1586 const wxString::const_iterator end = fmt.end();
1587 wxString::const_iterator lastSep = end;
1588 for ( wxString::const_iterator p = fmt.begin(); p != end; ++p )
1589 {
1590 if ( strchr(timeSep, *p) )
1591 {
1592 if ( lastSep == end )
1593 lastSep = p;
1594
1595 // skip it for now, we'll discard it if it's followed by a time
1596 // specifier later or add it to fmtDateOnly if it is not
1597 continue;
1598 }
1599
1600 if ( *p == '%' &&
1601 (p + 1 != end) && strchr(timeFmtSpecs, p[1]) )
1602 {
1603 // time specified found: skip it and any preceding separators
1604 ++p;
1605 lastSep = end;
1606 continue;
1607 }
1608
1609 if ( lastSep != end )
1610 {
1611 fmtDateOnly += wxString(lastSep, p);
1612 lastSep = end;
1613 }
1614
1615 fmtDateOnly += *p;
1616 }
1617
1618 return fmtDateOnly;
1619#else // !HAVE_LANGINFO_H
c5147528
VZ
1620 wxUnusedVar(index);
1621
89a7e1ff
VZ
1622 // no fallback, let the application deal with unavailability of
1623 // nl_langinfo() itself as there is no good way for us to do it (well, we
1624 // could try to reverse engineer the format from strftime() output but this
1625 // looks like too much trouble considering the relatively small number of
1626 // systems without nl_langinfo() still in use)
1627 return wxString();
1628#endif // HAVE_LANGINFO_H/!HAVE_LANGINFO_H
1629}
1630
1631} // anonymous namespace
cfb20656
VZ
1632
1633/* static */
60d876f3 1634wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory cat)
cfb20656 1635{
f3342781
VZ
1636 lconv * const lc = localeconv();
1637 if ( !lc )
1638 return wxString();
1639
89a7e1ff 1640 switch ( index )
60d876f3 1641 {
89a7e1ff
VZ
1642 case wxLOCALE_THOUSANDS_SEP:
1643 if ( cat == wxLOCALE_CAT_NUMBER )
1644 return lc->thousands_sep;
1645 else if ( cat == wxLOCALE_CAT_MONEY )
1646 return lc->mon_thousands_sep;
f3342781 1647
89a7e1ff 1648 wxFAIL_MSG( "invalid wxLocaleCategory" );
f3342781
VZ
1649 break;
1650
f3342781 1651
89a7e1ff
VZ
1652 case wxLOCALE_DECIMAL_POINT:
1653 if ( cat == wxLOCALE_CAT_NUMBER )
1654 return lc->decimal_point;
1655 else if ( cat == wxLOCALE_CAT_MONEY )
1656 return lc->mon_decimal_point;
1657
1658 wxFAIL_MSG( "invalid wxLocaleCategory" );
f3342781
VZ
1659 break;
1660
89a7e1ff
VZ
1661 case wxLOCALE_SHORT_DATE_FMT:
1662 case wxLOCALE_LONG_DATE_FMT:
1663 case wxLOCALE_DATE_TIME_FMT:
1664 case wxLOCALE_TIME_FMT:
1665 if ( cat != wxLOCALE_CAT_DATE && cat != wxLOCALE_CAT_DEFAULT )
1666 {
1667 wxFAIL_MSG( "invalid wxLocaleCategory" );
1668 break;
1669 }
1670
1671 return GetDateFormatFromLangInfo(index);
1672
1673
60d876f3 1674 default:
89a7e1ff 1675 wxFAIL_MSG( "unknown wxLocaleInfo value" );
60d876f3 1676 }
f3342781 1677
f3342781 1678 return wxString();
b7b97e77 1679}
cfb20656 1680
dff6cf57 1681#endif // platform
cfb20656 1682
c801d85f
KB
1683// ----------------------------------------------------------------------------
1684// global functions and variables
1685// ----------------------------------------------------------------------------
1686
c801d85f
KB
1687// retrieve/change current locale
1688// ------------------------------
1689
1690// the current locale object
84c18814 1691static wxLocale *g_pLocale = NULL;
c801d85f 1692
1678ad78
GL
1693wxLocale *wxGetLocale()
1694{
ca642c8a 1695 return g_pLocale;
1678ad78
GL
1696}
1697
c801d85f
KB
1698wxLocale *wxSetLocale(wxLocale *pLocale)
1699{
ca642c8a
FM
1700 wxLocale *pOld = g_pLocale;
1701 g_pLocale = pLocale;
1702 return pOld;
c801d85f 1703}
d427503c 1704
41780009
VS
1705
1706
1707// ----------------------------------------------------------------------------
1708// wxLocale module (for lazy destruction of languagesDB)
1709// ----------------------------------------------------------------------------
1710
1711class wxLocaleModule: public wxModule
1712{
1713 DECLARE_DYNAMIC_CLASS(wxLocaleModule)
1714 public:
1715 wxLocaleModule() {}
18e065b4
VS
1716
1717 bool OnInit()
1718 {
1719 return true;
1720 }
1721
1722 void OnExit()
1723 {
18e065b4
VS
1724 wxLocale::DestroyLanguagesDB();
1725 }
41780009
VS
1726};
1727
1728IMPLEMENT_DYNAMIC_CLASS(wxLocaleModule, wxModule)
1729
d427503c 1730#endif // wxUSE_INTL