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