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