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