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