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