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) 
   9 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
  10 // Licence:     wxWindows licence 
  11 ///////////////////////////////////////////////////////////////////////////// 
  13 // ============================================================================ 
  15 // ============================================================================ 
  17 // ---------------------------------------------------------------------------- 
  19 // ---------------------------------------------------------------------------- 
  21 // For compilers that support precompilation, includes "wx.h". 
  22 #include "wx/wxprec.h" 
  29 // The following define is needed by Innotek's libc to 
  30 // make the definition of struct localeconv available. 
  31 #define __INTERNAL_DEFS 
  37     #include "wx/dynarray.h" 
  38     #include "wx/string.h" 
  43     #include "wx/hashmap.h" 
  44     #include "wx/module.h" 
  54 #ifdef HAVE_LANGINFO_H 
  59     #include "wx/msw/private.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" 
  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" 
  78 // ---------------------------------------------------------------------------- 
  80 // ---------------------------------------------------------------------------- 
  82 #define TRACE_I18N wxS("i18n") 
  84 // ============================================================================ 
  86 // ============================================================================ 
  88 // ---------------------------------------------------------------------------- 
  90 // ---------------------------------------------------------------------------- 
  92 static wxLocale 
*wxSetLocale(wxLocale 
*pLocale
); 
  97 // get just the language part ("en" in "en_GB") 
  98 inline wxString 
ExtractLang(const wxString
& langFull
) 
 100     return langFull
.BeforeFirst('_'); 
 103 // helper functions of GetSystemLanguage() 
 106 // get everything else (including the leading '_') 
 107 inline wxString 
ExtractNotLang(const wxString
& langFull
) 
 109     size_t pos 
= langFull
.find('_'); 
 110     if ( pos 
!= wxString::npos 
) 
 111         return langFull
.substr(pos
); 
 118 } // anonymous namespace 
 120 // ---------------------------------------------------------------------------- 
 122 // ---------------------------------------------------------------------------- 
 126 // helper used by wxLanguageInfo::GetLocaleName() and elsewhere to determine 
 127 // whether the locale is Unicode-only (it is if this function returns empty 
 129 static wxString 
wxGetANSICodePageForLocale(LCID lcid
) 
 134     if ( ::GetLocaleInfo(lcid
, LOCALE_IDEFAULTANSICODEPAGE
, 
 135                         buffer
, WXSIZEOF(buffer
)) > 0 ) 
 137         if ( buffer
[0] != wxT('0') || buffer
[1] != wxT('\0') ) 
 139         //else: this locale doesn't use ANSI code page 
 145 wxUint32 
wxLanguageInfo::GetLCID() const 
 147     return MAKELCID(MAKELANGID(WinLang
, WinSublang
), SORT_DEFAULT
); 
 150 wxString 
wxLanguageInfo::GetLocaleName() const 
 154     const LCID lcid 
= GetLCID(); 
 157     buffer
[0] = wxT('\0'); 
 158     if ( !::GetLocaleInfo(lcid
, LOCALE_SENGLANGUAGE
, buffer
, WXSIZEOF(buffer
)) ) 
 160         wxLogLastError(wxT("GetLocaleInfo(LOCALE_SENGLANGUAGE)")); 
 165     if ( ::GetLocaleInfo(lcid
, LOCALE_SENGCOUNTRY
, 
 166                         buffer
, WXSIZEOF(buffer
)) > 0 ) 
 168         locale 
<< wxT('_') << buffer
; 
 171     const wxString cp 
= wxGetANSICodePageForLocale(lcid
); 
 174         locale 
<< wxT('.') << cp
; 
 182 // ---------------------------------------------------------------------------- 
 184 // ---------------------------------------------------------------------------- 
 186 #include "wx/arrimpl.cpp" 
 187 WX_DECLARE_EXPORTED_OBJARRAY(wxLanguageInfo
, wxLanguageInfoArray
); 
 188 WX_DEFINE_OBJARRAY(wxLanguageInfoArray
) 
 190 wxLanguageInfoArray 
*wxLocale::ms_languagesDB 
= NULL
; 
 192 /*static*/ void wxLocale::CreateLanguagesDB() 
 194     if (ms_languagesDB 
== NULL
) 
 196         ms_languagesDB 
= new wxLanguageInfoArray
; 
 201 /*static*/ void wxLocale::DestroyLanguagesDB() 
 203     wxDELETE(ms_languagesDB
); 
 207 void wxLocale::DoCommonInit() 
 209     m_pszOldLocale 
= NULL
; 
 211     m_pOldLocale 
= wxSetLocale(this); 
 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 
 220     //         wxLocale loc_cs(wxLANGUAGE_CZECH); 
 221     //         // _() returns Czech 
 223     //     // _() returns French again 
 224     wxTranslations 
*oldTrans 
= wxTranslations::Get(); 
 226          (m_pOldLocale 
&& oldTrans 
== &m_pOldLocale
->m_translations
) ) 
 228         wxTranslations::SetNonOwned(&m_translations
); 
 231     m_language 
= wxLANGUAGE_UNKNOWN
; 
 232     m_initialized 
= false; 
 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
, 
 240 #if WXWIN_COMPATIBILITY_2_8 
 241                    ,bool            WXUNUSED_UNLESS_DEBUG(bConvertEncoding
) 
 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") ); 
 250     bool ret 
= DoInit(name
, shortName
, locale
); 
 252     // NB: don't use 'lang' here, 'language' may be wxLANGUAGE_DEFAULT 
 253     wxTranslations 
*t 
= wxTranslations::Get(); 
 256         t
->SetLanguage(shortName
); 
 265 bool wxLocale::DoInit(const wxString
& name
, 
 266                       const wxString
& shortName
, 
 267                       const wxString
& locale
) 
 269     wxASSERT_MSG( !m_initialized
, 
 270                     wxS("you can't call wxLocale::Init more than once") ); 
 272     m_initialized 
= true; 
 274     m_strShort 
= shortName
; 
 275     m_language 
= wxLANGUAGE_UNKNOWN
; 
 277     // change current locale (default: same as long name) 
 278     wxString 
szLocale(locale
); 
 279     if ( szLocale
.empty() ) 
 281         // the argument to setlocale() 
 282         szLocale 
= shortName
; 
 284         wxCHECK_MSG( !szLocale
.empty(), false, 
 285                     wxS("no locale to set in wxLocale::Init()") ); 
 288     const char *oldLocale 
= wxSetlocale(LC_ALL
, szLocale
); 
 290         m_pszOldLocale 
= wxStrdup(oldLocale
); 
 292         m_pszOldLocale 
= NULL
; 
 294     if ( m_pszOldLocale 
== NULL 
) 
 296         wxLogError(_("locale '%s' cannot be set."), szLocale
); 
 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() ) 
 306             m_strShort 
+= (wxChar
)wxTolower(szLocale
[0]); 
 307             if ( szLocale
.length() > 1 ) 
 308                 m_strShort 
+= (wxChar
)wxTolower(szLocale
[1]); 
 316 #if defined(__UNIX__) && wxUSE_UNICODE && !defined(__WXMAC__) 
 317 static const char *wxSetlocaleTryUTF8(int c
, const wxString
& lc
) 
 319     const char *l 
= NULL
; 
 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 
 328         buf2 
= buf 
+ wxS(".UTF-8"); 
 329         l 
= wxSetlocale(c
, buf2
); 
 332             buf2 
= buf 
+ wxS(".utf-8"); 
 333             l 
= wxSetlocale(c
, buf2
); 
 337             buf2 
= buf 
+ wxS(".UTF8"); 
 338             l 
= wxSetlocale(c
, buf2
); 
 342             buf2 
= buf 
+ wxS(".utf8"); 
 343             l 
= wxSetlocale(c
, buf2
); 
 347     // if we can't set UTF-8 locale, try non-UTF-8 one: 
 349         l 
= wxSetlocale(c
, lc
); 
 354 #define wxSetlocaleTryUTF8(c, lc)  wxSetlocale(c, lc) 
 357 bool wxLocale::Init(int language
, int flags
) 
 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") ); 
 367     if (lang 
== wxLANGUAGE_DEFAULT
) 
 369         // auto detect the language 
 370         lang 
= GetSystemLanguage(); 
 373     // We failed to detect system language, so we will use English: 
 374     if (lang 
== wxLANGUAGE_UNKNOWN
) 
 379     const wxLanguageInfo 
*info 
= GetLanguageInfo(lang
); 
 384         wxLogError(wxS("Unknown language %i."), lang
); 
 388     wxString name 
= info
->Description
; 
 389     wxString canonical 
= info
->CanonicalName
; 
 394     const char *retloc 
= wxSetlocale(LC_ALL 
, wxEmptyString
); 
 395 #elif defined(__UNIX__) && !defined(__WXMAC__) 
 396     if (language 
!= wxLANGUAGE_DEFAULT
) 
 397         locale 
= info
->CanonicalName
; 
 399     const char *retloc 
= wxSetlocaleTryUTF8(LC_ALL
, locale
); 
 401     const wxString langOnly 
= ExtractLang(locale
); 
 404         // Some C libraries don't like xx_YY form and require xx only 
 405         retloc 
= wxSetlocaleTryUTF8(LC_ALL
, langOnly
); 
 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 
 413     // TODO: add encodings applicable to each language to the lang DB and try 
 414     //       them all in turn here 
 417         const wxChar 
**names 
= 
 418             wxFontMapperBase::GetAllEncodingNames(wxFONTENCODING_UTF8
); 
 421             retloc 
= wxSetlocale(LC_ALL
, locale 
+ wxS('.') + *names
++); 
 426 #endif // wxUSE_FONTMAP 
 430         // Some C libraries (namely glibc) still use old ISO 639, 
 431         // so will translate the abbrev for them 
 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"); 
 444         if ( !localeAlt
.empty() ) 
 446             retloc 
= wxSetlocaleTryUTF8(LC_ALL
, localeAlt
); 
 448                 retloc 
= wxSetlocaleTryUTF8(LC_ALL
, ExtractLang(localeAlt
)); 
 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 
 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
, ' ')); 
 468 #elif defined(__WIN32__) 
 469     const char *retloc 
= "C"; 
 470     if ( language 
!= wxLANGUAGE_DEFAULT 
) 
 472         if ( info
->WinLang 
== 0 ) 
 474             wxLogWarning(wxS("Locale '%s' not supported by OS."), name
.c_str()); 
 475             // retloc already set to "C" 
 477         else // language supported by Windows 
 479             // Windows CE doesn't have SetThreadLocale() and there doesn't seem 
 480             // to be any equivalent 
 482             const wxUint32 lcid 
= info
->GetLCID(); 
 484             // change locale used by Windows functions 
 485             ::SetThreadLocale(lcid
); 
 488             // and also call setlocale() to change locale used by the CRT 
 489             locale 
= info
->GetLocaleName(); 
 490             if ( locale
.empty() ) 
 494             else // have a valid locale 
 496                 retloc 
= wxSetlocale(LC_ALL
, locale
); 
 500     else // language == wxLANGUAGE_DEFAULT 
 502         retloc 
= wxSetlocale(LC_ALL
, wxEmptyString
); 
 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. 
 512         if ( wxGetANSICodePageForLocale(LOCALE_USER_DEFAULT
).empty() ) 
 514             // we set the locale to a Unicode-only language, don't treat the 
 515             // inability of CRT to use it as an error 
 519 #endif // CRT not handling Unicode-only languages 
 523 #elif defined(__WXMAC__) 
 524     if (lang 
== wxLANGUAGE_DEFAULT
) 
 525         locale 
= wxEmptyString
; 
 527         locale 
= info
->CanonicalName
; 
 529     const char *retloc 
= wxSetlocale(LC_ALL
, locale
); 
 533         // Some C libraries don't like xx_YY form and require xx only 
 534         retloc 
= wxSetlocale(LC_ALL
, ExtractLang(locale
)); 
 539     #define WX_NO_LOCALE_SUPPORT 
 542 #ifndef WX_NO_LOCALE_SUPPORT 
 545         wxLogWarning(_("Cannot set locale to language \"%s\"."), name
.c_str()); 
 547         // continue nevertheless and try to load at least the translations for 
 551     if ( !DoInit(name
, canonical
, retloc
) ) 
 556     if (IsOk()) // setlocale() succeeded 
 559     // NB: don't use 'lang' here, 'language' 
 560     wxTranslations 
*t 
= wxTranslations::Get(); 
 563         t
->SetLanguage(static_cast<wxLanguage
>(language
)); 
 565         if ( flags 
& wxLOCALE_LOAD_DEFAULT 
) 
 570 #endif // !WX_NO_LOCALE_SUPPORT 
 573 /*static*/ int wxLocale::GetSystemLanguage() 
 577     // init i to avoid compiler warning 
 579         count 
= ms_languagesDB
->GetCount(); 
 581 #if defined(__UNIX__) 
 582     // first get the string identifying the language from the environment 
 585     wxCFRef
<CFLocaleRef
> userLocaleRef(CFLocaleCopyCurrent()); 
 587     // because the locale identifier (kCFLocaleIdentifier) is formatted a little bit differently, eg 
 588     // az_Cyrl_AZ@calendar=buddhist;currency=JPY we just recreate the base info as expected by wx here 
 590     wxCFStringRef 
str(wxCFRetain((CFStringRef
)CFLocaleGetValue(userLocaleRef
, kCFLocaleLanguageCode
))); 
 591     langFull 
= str
.AsString()+"_"; 
 592     str
.reset(wxCFRetain((CFStringRef
)CFLocaleGetValue(userLocaleRef
, kCFLocaleCountryCode
))); 
 593     langFull 
+= str
.AsString(); 
 595     if (!wxGetEnv(wxS("LC_ALL"), &langFull
) && 
 596         !wxGetEnv(wxS("LC_MESSAGES"), &langFull
) && 
 597         !wxGetEnv(wxS("LANG"), &langFull
)) 
 599         // no language specified, treat it as English 
 600         return wxLANGUAGE_ENGLISH_US
; 
 603     if ( langFull 
== wxS("C") || langFull 
== wxS("POSIX") ) 
 605         // default C locale is English too 
 606         return wxLANGUAGE_ENGLISH_US
; 
 610     // the language string has the following form 
 612     //      lang[_LANG][.encoding][@modifier] 
 614     // (see environ(5) in the Open Unix specification) 
 616     // where lang is the primary language, LANG is a sublang/territory, 
 617     // encoding is the charset to use and modifier "allows the user to select 
 618     // a specific instance of localization data within a single category" 
 620     // for example, the following strings are valid: 
 625     //      de_DE.iso88591@euro 
 627     // for now we don't use the encoding, although we probably should (doing 
 628     // translations of the msg catalogs on the fly as required) (TODO) 
 630     // we need the modified for languages like Valencian: ca_ES@valencia 
 631     // though, remember it 
 633     size_t posModifier 
= langFull
.find_first_of(wxS("@")); 
 634     if ( posModifier 
!= wxString::npos 
) 
 635         modifier 
= langFull
.Mid(posModifier
); 
 637     size_t posEndLang 
= langFull
.find_first_of(wxS("@.")); 
 638     if ( posEndLang 
!= wxString::npos 
) 
 640         langFull
.Truncate(posEndLang
); 
 643     // do we have just the language (or sublang too)? 
 644     const bool justLang 
= langFull
.find('_') == wxString::npos
; 
 646     // 0. Make sure the lang is according to latest ISO 639 
 647     //    (this is necessary because glibc uses iw and in instead 
 648     //    of he and id respectively). 
 650     // the language itself (second part is the dialect/sublang) 
 651     wxString langOrig 
= ExtractLang(langFull
); 
 654     if ( langOrig 
== wxS("iw")) 
 656     else if (langOrig 
== wxS("in")) 
 658     else if (langOrig 
== wxS("ji")) 
 660     else if (langOrig 
== wxS("no_NO")) 
 662     else if (langOrig 
== wxS("no_NY")) 
 664     else if (langOrig 
== wxS("no")) 
 670     if ( lang 
!= langOrig 
) 
 672         langFull 
= lang 
+ ExtractNotLang(langFull
); 
 675     // 1. Try to find the language either as is: 
 676     // a) With modifier if set 
 677     if ( !modifier
.empty() ) 
 679         wxString langFullWithModifier 
= langFull 
+ modifier
; 
 680         for ( i 
= 0; i 
< count
; i
++ ) 
 682             if ( ms_languagesDB
->Item(i
).CanonicalName 
== langFullWithModifier 
) 
 687     // b) Without modifier 
 688     if ( modifier
.empty() || i 
== count 
) 
 690         for ( i 
= 0; i 
< count
; i
++ ) 
 692             if ( ms_languagesDB
->Item(i
).CanonicalName 
== langFull 
) 
 697     // 2. If langFull is of the form xx_YY, try to find xx: 
 698     if ( i 
== count 
&& !justLang 
) 
 700         for ( i 
= 0; i 
< count
; i
++ ) 
 702             if ( ms_languagesDB
->Item(i
).CanonicalName 
== lang 
) 
 709     // 3. If langFull is of the form xx, try to find any xx_YY record: 
 710     if ( i 
== count 
&& justLang 
) 
 712         for ( i 
= 0; i 
< count
; i
++ ) 
 714             if ( ExtractLang(ms_languagesDB
->Item(i
).CanonicalName
) 
 725         // In addition to the format above, we also can have full language 
 726         // names in LANG env var - for example, SuSE is known to use 
 727         // LANG="german" - so check for use of non-standard format and try to 
 728         // find the name in verbose description. 
 729         for ( i 
= 0; i 
< count
; i
++ ) 
 731             if (ms_languagesDB
->Item(i
).Description
.CmpNoCase(langFull
) == 0) 
 737 #elif defined(__WIN32__) 
 738     LCID lcid 
= GetUserDefaultLCID(); 
 741         wxUint32 lang 
= PRIMARYLANGID(LANGIDFROMLCID(lcid
)); 
 742         wxUint32 sublang 
= SUBLANGID(LANGIDFROMLCID(lcid
)); 
 744         for ( i 
= 0; i 
< count
; i
++ ) 
 746             if (ms_languagesDB
->Item(i
).WinLang 
== lang 
&& 
 747                 ms_languagesDB
->Item(i
).WinSublang 
== sublang
) 
 753     //else: leave wxlang == wxLANGUAGE_UNKNOWN 
 758         // we did find a matching entry, use it 
 759         return ms_languagesDB
->Item(i
).Language
; 
 762     // no info about this language in the database 
 763     return wxLANGUAGE_UNKNOWN
; 
 766 // ---------------------------------------------------------------------------- 
 768 // ---------------------------------------------------------------------------- 
 770 // this is a bit strange as under Windows we get the encoding name using its 
 771 // numeric value and under Unix we do it the other way round, but this just 
 772 // reflects the way different systems provide the encoding info 
 775 wxString 
wxLocale::GetSystemEncodingName() 
 779 #if defined(__WIN32__) && !defined(__WXMICROWIN__) 
 780     // FIXME: what is the error return value for GetACP()? 
 781     UINT codepage 
= ::GetACP(); 
 782     encname
.Printf(wxS("windows-%u"), codepage
); 
 783 #elif defined(__WXMAC__) 
 784     // default is just empty string, this resolves to the default system 
 786 #elif defined(__UNIX_LIKE__) 
 788 #if defined(HAVE_LANGINFO_H) && defined(CODESET) 
 789     // GNU libc provides current character set this way (this conforms 
 791     char *oldLocale 
= strdup(setlocale(LC_CTYPE
, NULL
)); 
 792     setlocale(LC_CTYPE
, ""); 
 793     const char *alang 
= nl_langinfo(CODESET
); 
 794     setlocale(LC_CTYPE
, oldLocale
); 
 799         encname 
= wxString::FromAscii( alang 
); 
 801     else // nl_langinfo() failed 
 802 #endif // HAVE_LANGINFO_H 
 804         // if we can't get at the character set directly, try to see if it's in 
 805         // the environment variables (in most cases this won't work, but I was 
 807         char *lang 
= getenv( "LC_ALL"); 
 808         char *dot 
= lang 
? strchr(lang
, '.') : NULL
; 
 811             lang 
= getenv( "LC_CTYPE" ); 
 813                 dot 
= strchr(lang
, '.' ); 
 817             lang 
= getenv( "LANG"); 
 819                 dot 
= strchr(lang
, '.'); 
 824             encname 
= wxString::FromAscii( dot
+1 ); 
 833 wxFontEncoding 
wxLocale::GetSystemEncoding() 
 835 #if defined(__WIN32__) && !defined(__WXMICROWIN__) 
 836     UINT codepage 
= ::GetACP(); 
 838     // wxWidgets only knows about CP1250-1257, 874, 932, 936, 949, 950 
 839     if ( codepage 
>= 1250 && codepage 
<= 1257 ) 
 841         return (wxFontEncoding
)(wxFONTENCODING_CP1250 
+ codepage 
- 1250); 
 844     if ( codepage 
== 874 ) 
 846         return wxFONTENCODING_CP874
; 
 849     if ( codepage 
== 932 ) 
 851         return wxFONTENCODING_CP932
; 
 854     if ( codepage 
== 936 ) 
 856         return wxFONTENCODING_CP936
; 
 859     if ( codepage 
== 949 ) 
 861         return wxFONTENCODING_CP949
; 
 864     if ( codepage 
== 950 ) 
 866         return wxFONTENCODING_CP950
; 
 868 #elif defined(__WXMAC__) 
 869     CFStringEncoding encoding 
= 0 ; 
 870     encoding 
= CFStringGetSystemEncoding() ; 
 871     return wxMacGetFontEncFromSystemEnc( encoding 
) ; 
 872 #elif defined(__UNIX_LIKE__) && wxUSE_FONTMAP 
 873     const wxString encname 
= GetSystemEncodingName(); 
 874     if ( !encname
.empty() ) 
 876         wxFontEncoding enc 
= wxFontMapperBase::GetEncodingFromName(encname
); 
 878         // on some modern Linux systems (RedHat 8) the default system locale 
 879         // is UTF8 -- but it isn't supported by wxGTK1 in ANSI build at all so 
 880         // don't even try to use it in this case 
 881 #if !wxUSE_UNICODE && \ 
 882         ((defined(__WXGTK__) && !defined(__WXGTK20__)) || defined(__WXMOTIF__)) 
 883         if ( enc 
== wxFONTENCODING_UTF8 
) 
 885             // the most similar supported encoding... 
 886             enc 
= wxFONTENCODING_ISO8859_1
; 
 888 #endif // !wxUSE_UNICODE 
 890         // GetEncodingFromName() returns wxFONTENCODING_DEFAULT for C locale 
 891         // (a.k.a. US-ASCII) which is arguably a bug but keep it like this for 
 892         // backwards compatibility and just take care to not return 
 893         // wxFONTENCODING_DEFAULT from here as this surely doesn't make sense 
 894         if ( enc 
== wxFONTENCODING_DEFAULT 
) 
 896             // we don't have wxFONTENCODING_ASCII, so use the closest one 
 897             return wxFONTENCODING_ISO8859_1
; 
 900         if ( enc 
!= wxFONTENCODING_MAX 
) 
 904         //else: return wxFONTENCODING_SYSTEM below 
 908     return wxFONTENCODING_SYSTEM
; 
 912 void wxLocale::AddLanguage(const wxLanguageInfo
& info
) 
 915     ms_languagesDB
->Add(info
); 
 919 const wxLanguageInfo 
*wxLocale::GetLanguageInfo(int lang
) 
 923     // calling GetLanguageInfo(wxLANGUAGE_DEFAULT) is a natural thing to do, so 
 925     if ( lang 
== wxLANGUAGE_DEFAULT 
) 
 926         lang 
= GetSystemLanguage(); 
 928     const size_t count 
= ms_languagesDB
->GetCount(); 
 929     for ( size_t i 
= 0; i 
< count
; i
++ ) 
 931         if ( ms_languagesDB
->Item(i
).Language 
== lang 
) 
 933             // We need to create a temporary here in order to make this work with BCC in final build mode 
 934             wxLanguageInfo 
*ptr 
= &ms_languagesDB
->Item(i
); 
 943 wxString 
wxLocale::GetLanguageName(int lang
) 
 945     if ( lang 
== wxLANGUAGE_DEFAULT 
|| lang 
== wxLANGUAGE_UNKNOWN 
) 
 946         return wxEmptyString
; 
 948     const wxLanguageInfo 
*info 
= GetLanguageInfo(lang
); 
 950         return wxEmptyString
; 
 952         return info
->Description
; 
 956 wxString 
wxLocale::GetLanguageCanonicalName(int lang
) 
 958     if ( lang 
== wxLANGUAGE_DEFAULT 
|| lang 
== wxLANGUAGE_UNKNOWN 
) 
 959         return wxEmptyString
; 
 961     const wxLanguageInfo 
*info 
= GetLanguageInfo(lang
); 
 963         return wxEmptyString
; 
 965         return info
->CanonicalName
; 
 969 const wxLanguageInfo 
*wxLocale::FindLanguageInfo(const wxString
& locale
) 
 973     const wxLanguageInfo 
*infoRet 
= NULL
; 
 975     const size_t count 
= ms_languagesDB
->GetCount(); 
 976     for ( size_t i 
= 0; i 
< count
; i
++ ) 
 978         const wxLanguageInfo 
*info 
= &ms_languagesDB
->Item(i
); 
 980         if ( wxStricmp(locale
, info
->CanonicalName
) == 0 || 
 981                 wxStricmp(locale
, info
->Description
) == 0 ) 
 983             // exact match, stop searching 
 988         if ( wxStricmp(locale
, info
->CanonicalName
.BeforeFirst(wxS('_'))) == 0 ) 
 990             // a match -- but maybe we'll find an exact one later, so continue 
 993             // OTOH, maybe we had already found a language match and in this 
 994             // case don't overwrite it because the entry for the default 
 995             // country always appears first in ms_languagesDB 
1004 wxString 
wxLocale::GetSysName() const 
1006     return wxSetlocale(LC_ALL
, NULL
); 
1010 wxLocale::~wxLocale() 
1012     // Restore old translations object. 
1013     // See DoCommonInit() for explanation of why this is needed for backward 
1015     if ( wxTranslations::Get() == &m_translations 
) 
1018             wxTranslations::SetNonOwned(&m_pOldLocale
->m_translations
); 
1020             wxTranslations::Set(NULL
); 
1023     // restore old locale pointer 
1024     wxSetLocale(m_pOldLocale
); 
1026     wxSetlocale(LC_ALL
, m_pszOldLocale
); 
1027     free(const_cast<char *>(m_pszOldLocale
)); 
1031 // check if the given locale is provided by OS and C run time 
1033 bool wxLocale::IsAvailable(int lang
) 
1035     const wxLanguageInfo 
*info 
= wxLocale::GetLanguageInfo(lang
); 
1038         // The language is unknown (this normally only happens when we're 
1039         // passed wxLANGUAGE_DEFAULT), so we can't support it. 
1040         wxASSERT_MSG( lang 
== wxLANGUAGE_DEFAULT
, 
1041                       wxS("No info for a valid language?") ); 
1045 #if defined(__WIN32__) 
1046     if ( !info
->WinLang 
) 
1049     if ( !::IsValidLocale(info
->GetLCID(), LCID_INSTALLED
) ) 
1052 #elif defined(__UNIX__) 
1054     // Test if setting the locale works, then set it back. 
1055     char * const oldLocale 
= wxStrdupA(setlocale(LC_ALL
, NULL
)); 
1057     // Some platforms don't like xx_YY form and require xx only so test for 
1060         available 
= wxSetlocaleTryUTF8(LC_ALL
, info
->CanonicalName
) || 
1061                     wxSetlocaleTryUTF8(LC_ALL
, ExtractLang(info
->CanonicalName
)); 
1063     // restore the original locale 
1064     wxSetlocale(LC_ALL
, oldLocale
); 
1076 bool wxLocale::AddCatalog(const wxString
& domain
) 
1078     wxTranslations 
*t 
= wxTranslations::Get(); 
1081     return t
->AddCatalog(domain
); 
1084 bool wxLocale::AddCatalog(const wxString
& domain
, wxLanguage msgIdLanguage
) 
1086     wxTranslations 
*t 
= wxTranslations::Get(); 
1089     return t
->AddCatalog(domain
, msgIdLanguage
); 
1092 // add a catalog to our linked list 
1093 bool wxLocale::AddCatalog(const wxString
& szDomain
, 
1094                         wxLanguage      msgIdLanguage
, 
1095                         const wxString
& msgIdCharset
) 
1097     wxTranslations 
*t 
= wxTranslations::Get(); 
1101     wxUnusedVar(msgIdCharset
); 
1102     return t
->AddCatalog(szDomain
, msgIdLanguage
); 
1104     return t
->AddCatalog(szDomain
, msgIdLanguage
, msgIdCharset
); 
1108 bool wxLocale::IsLoaded(const wxString
& domain
) const 
1110     wxTranslations 
*t 
= wxTranslations::Get(); 
1113     return t
->IsLoaded(domain
); 
1116 wxString 
wxLocale::GetHeaderValue(const wxString
& header
, 
1117                                   const wxString
& domain
) const 
1119     wxTranslations 
*t 
= wxTranslations::Get(); 
1121         return wxEmptyString
; 
1122     return t
->GetHeaderValue(header
, domain
); 
1125 // ---------------------------------------------------------------------------- 
1126 // accessors for locale-dependent data 
1127 // ---------------------------------------------------------------------------- 
1129 #if defined(__WXMSW__) || defined(__WXOSX__) 
1134 // This function translates from Unicode date formats described at 
1136 //      http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns 
1138 // to strftime()-like syntax. This translation is not lossless but we try to do 
1141 static wxString 
TranslateFromUnicodeFormat(const wxString
& fmt
) 
1144     fmtWX
.reserve(fmt
.length()); 
1147     size_t lastCount 
= 0; 
1149     const char* formatchars 
= 
1157     for ( wxString::const_iterator p 
= fmt
.begin(); /* end handled inside */; ++p 
) 
1159         if ( p 
!= fmt
.end() ) 
1167             const wxUniChar ch 
= (*p
).GetValue(); 
1168             if ( ch
.IsAscii() && strchr(formatchars
, ch
) ) 
1170                 // these characters come in groups, start counting them 
1177         // interpret any special characters we collected so far 
1183                     switch ( lastCount 
) 
1187                             // these two are the same as we don't distinguish 
1188                             // between 1 and 2 digits for days 
1201                             wxFAIL_MSG( "too many 'd's" ); 
1206                     switch ( lastCount 
) 
1215                             wxFAIL_MSG( "wrong number of 'D's" ); 
1219                     switch ( lastCount 
) 
1227                             wxFAIL_MSG( "wrong number of 'w's" ); 
1231                    switch ( lastCount 
) 
1246                             wxFAIL_MSG( "wrong number of 'E's" ); 
1251                     switch ( lastCount 
) 
1255                             // as for 'd' and 'dd' above 
1268                             wxFAIL_MSG( "too many 'M's" ); 
1273                     switch ( lastCount 
) 
1285                             wxFAIL_MSG( "wrong number of 'y's" ); 
1290                     switch ( lastCount 
) 
1298                             wxFAIL_MSG( "wrong number of 'H's" ); 
1303                     switch ( lastCount 
) 
1311                             wxFAIL_MSG( "wrong number of 'h's" ); 
1316                     switch ( lastCount 
) 
1324                             wxFAIL_MSG( "wrong number of 'm's" ); 
1329                     switch ( lastCount 
) 
1337                             wxFAIL_MSG( "wrong number of 's's" ); 
1342                     // strftime() doesn't have era string, 
1343                     // ignore this format 
1344                     wxASSERT_MSG( lastCount 
<= 2, "too many 'g's" ); 
1354                     switch ( lastCount 
) 
1362                             wxFAIL_MSG( "too many 't's" ); 
1367                     wxFAIL_MSG( "unreachable" ); 
1374         if ( p 
== fmt
.end() ) 
1377         // not a special character so must be just a separator, treat as is 
1378         if ( *p 
== wxT('%') ) 
1380             // this one needs to be escaped 
1390 } // anonymous namespace 
1392 #endif // __WXMSW__ || __WXOSX__ 
1394 #if defined(__WXMSW__) 
1399 LCTYPE 
GetLCTYPEFormatFromLocalInfo(wxLocaleInfo index
) 
1403         case wxLOCALE_SHORT_DATE_FMT
: 
1404             return LOCALE_SSHORTDATE
; 
1406         case wxLOCALE_LONG_DATE_FMT
: 
1407             return LOCALE_SLONGDATE
; 
1409         case wxLOCALE_TIME_FMT
: 
1410             return LOCALE_STIMEFORMAT
; 
1413             wxFAIL_MSG( "no matching LCTYPE" ); 
1419 } // anonymous namespace 
1422 wxString 
wxLocale::GetInfo(wxLocaleInfo index
, wxLocaleCategory cat
) 
1424     wxUint32 lcid 
= LOCALE_USER_DEFAULT
; 
1425     if ( wxGetLocale() ) 
1427         const wxLanguageInfo 
* const 
1428             info 
= GetLanguageInfo(wxGetLocale()->GetLanguage()); 
1430             lcid 
= info
->GetLCID(); 
1440         case wxLOCALE_THOUSANDS_SEP
: 
1441             if ( ::GetLocaleInfo(lcid
, LOCALE_STHOUSAND
, buf
, WXSIZEOF(buf
)) ) 
1445         case wxLOCALE_DECIMAL_POINT
: 
1446             if ( ::GetLocaleInfo(lcid
, 
1447                                  cat 
== wxLOCALE_CAT_MONEY
 
1448                                      ? LOCALE_SMONDECIMALSEP
 
1455                 // As we get our decimal point separator from Win32 and not the 
1456                 // CRT there is a possibility of mismatch between them and this 
1457                 // can easily happen if the user code called setlocale() 
1458                 // instead of using wxLocale to change the locale. And this can 
1459                 // result in very strange bugs elsewhere in the code as the 
1460                 // assumptions that formatted strings do use the decimal 
1461                 // separator actually fail, so check for it here. 
1464                     wxString::Format("%.3f", 1.23).find(str
) != wxString::npos
, 
1465                     "Decimal separator mismatch -- did you use setlocale()?" 
1466                     "If so, use wxLocale to change the locale instead." 
1471         case wxLOCALE_SHORT_DATE_FMT
: 
1472         case wxLOCALE_LONG_DATE_FMT
: 
1473         case wxLOCALE_TIME_FMT
: 
1474             if ( ::GetLocaleInfo(lcid
, GetLCTYPEFormatFromLocalInfo(index
), 
1475                                  buf
, WXSIZEOF(buf
)) ) 
1477                 return TranslateFromUnicodeFormat(buf
); 
1481         case wxLOCALE_DATE_TIME_FMT
: 
1482             // there doesn't seem to be any specific setting for this, so just 
1483             // combine date and time ones 
1485             // we use the short date because this is what "%c" uses by default 
1486             // ("%#c" uses long date but we have no way to specify the 
1487             // alternate representation here) 
1489                 const wxString datefmt 
= GetInfo(wxLOCALE_SHORT_DATE_FMT
); 
1490                 if ( datefmt
.empty() ) 
1493                 const wxString timefmt 
= GetInfo(wxLOCALE_TIME_FMT
); 
1494                 if ( timefmt
.empty() ) 
1497                 str 
<< datefmt 
<< ' ' << timefmt
; 
1502             wxFAIL_MSG( "unknown wxLocaleInfo" ); 
1508 #elif defined(__WXOSX__) 
1511 wxString 
wxLocale::GetInfo(wxLocaleInfo index
, wxLocaleCategory 
WXUNUSED(cat
)) 
1513     CFLocaleRef userLocaleRefRaw
; 
1514     if ( wxGetLocale() ) 
1516         userLocaleRefRaw 
= CFLocaleCreate
 
1518                                 kCFAllocatorDefault
, 
1519                                 wxCFStringRef(wxGetLocale()->GetCanonicalName()) 
1522     else // no current locale, use the default one 
1524         userLocaleRefRaw 
= CFLocaleCopyCurrent(); 
1527     wxCFRef
<CFLocaleRef
> userLocaleRef(userLocaleRefRaw
); 
1529     CFStringRef cfstr 
= 0; 
1532         case wxLOCALE_THOUSANDS_SEP
: 
1533             cfstr 
= (CFStringRef
) CFLocaleGetValue(userLocaleRef
, kCFLocaleGroupingSeparator
); 
1536         case wxLOCALE_DECIMAL_POINT
: 
1537             cfstr 
= (CFStringRef
) CFLocaleGetValue(userLocaleRef
, kCFLocaleDecimalSeparator
); 
1540         case wxLOCALE_SHORT_DATE_FMT
: 
1541         case wxLOCALE_LONG_DATE_FMT
: 
1542         case wxLOCALE_DATE_TIME_FMT
: 
1543         case wxLOCALE_TIME_FMT
: 
1545                 CFDateFormatterStyle dateStyle 
= kCFDateFormatterNoStyle
; 
1546                 CFDateFormatterStyle timeStyle 
= kCFDateFormatterNoStyle
; 
1549                     case wxLOCALE_SHORT_DATE_FMT
: 
1550                         dateStyle 
= kCFDateFormatterShortStyle
; 
1552                     case wxLOCALE_LONG_DATE_FMT
: 
1553                         dateStyle 
= kCFDateFormatterFullStyle
; 
1555                     case wxLOCALE_DATE_TIME_FMT
: 
1556                         dateStyle 
= kCFDateFormatterFullStyle
; 
1557                         timeStyle 
= kCFDateFormatterMediumStyle
; 
1559                     case wxLOCALE_TIME_FMT
: 
1560                         timeStyle 
= kCFDateFormatterMediumStyle
; 
1563                         wxFAIL_MSG( "unexpected time locale" ); 
1566                 wxCFRef
<CFDateFormatterRef
> dateFormatter( CFDateFormatterCreate
 
1567                     (NULL
, userLocaleRef
, dateStyle
, timeStyle
)); 
1568                 wxCFStringRef cfs 
= wxCFRetain( CFDateFormatterGetFormat(dateFormatter 
)); 
1569                 wxString format 
= TranslateFromUnicodeFormat(cfs
.AsString()); 
1570                 // we always want full years 
1571                 format
.Replace("%y","%Y"); 
1577             wxFAIL_MSG( "Unknown locale info" ); 
1581     wxCFStringRef 
str(wxCFRetain(cfstr
)); 
1582     return str
.AsString(); 
1585 #else // !__WXMSW__ && !__WXOSX__, assume generic POSIX 
1590 wxString 
GetDateFormatFromLangInfo(wxLocaleInfo index
) 
1592 #ifdef HAVE_LANGINFO_H 
1593     // array containing parameters for nl_langinfo() indexes by offset of index 
1594     // from wxLOCALE_SHORT_DATE_FMT 
1595     static const nl_item items
[] = 
1597         D_FMT
, D_T_FMT
, D_T_FMT
, T_FMT
, 
1600     const int nlidx 
= index 
- wxLOCALE_SHORT_DATE_FMT
; 
1601     if ( nlidx 
< 0 || nlidx 
>= (int)WXSIZEOF(items
) ) 
1603         wxFAIL_MSG( "logic error in GetInfo() code" ); 
1607     const wxString 
fmt(nl_langinfo(items
[nlidx
])); 
1609     // just return the format returned by nl_langinfo() except for long date 
1610     // format which we need to recover from date/time format ourselves (but not 
1611     // if we failed completely) 
1612     if ( fmt
.empty() || index 
!= wxLOCALE_LONG_DATE_FMT 
) 
1615     // this is not 100% precise but the idea is that a typical date/time format 
1616     // under POSIX systems is a combination of a long date format with time one 
1617     // so we should be able to get just the long date format by removing all 
1618     // time-specific format specifiers 
1619     static const char *timeFmtSpecs 
= "HIklMpPrRsSTXzZ"; 
1620     static const char *timeSep 
= " :./-"; 
1622     wxString fmtDateOnly
; 
1623     const wxString::const_iterator end 
= fmt
.end(); 
1624     wxString::const_iterator lastSep 
= end
; 
1625     for ( wxString::const_iterator p 
= fmt
.begin(); p 
!= end
; ++p 
) 
1627         if ( strchr(timeSep
, *p
) ) 
1629             if ( lastSep 
== end 
) 
1632             // skip it for now, we'll discard it if it's followed by a time 
1633             // specifier later or add it to fmtDateOnly if it is not 
1638                 (p 
+ 1 != end
) && strchr(timeFmtSpecs
, p
[1]) ) 
1640             // time specified found: skip it and any preceding separators 
1646         if ( lastSep 
!= end 
) 
1648             fmtDateOnly 
+= wxString(lastSep
, p
); 
1656 #else // !HAVE_LANGINFO_H 
1659     // no fallback, let the application deal with unavailability of 
1660     // nl_langinfo() itself as there is no good way for us to do it (well, we 
1661     // could try to reverse engineer the format from strftime() output but this 
1662     // looks like too much trouble considering the relatively small number of 
1663     // systems without nl_langinfo() still in use) 
1665 #endif // HAVE_LANGINFO_H/!HAVE_LANGINFO_H 
1668 } // anonymous namespace 
1671 wxString 
wxLocale::GetInfo(wxLocaleInfo index
, wxLocaleCategory cat
) 
1673     lconv 
* const lc 
= localeconv(); 
1679         case wxLOCALE_THOUSANDS_SEP
: 
1680             if ( cat 
== wxLOCALE_CAT_NUMBER 
) 
1681                 return lc
->thousands_sep
; 
1682             else if ( cat 
== wxLOCALE_CAT_MONEY 
) 
1683                 return lc
->mon_thousands_sep
; 
1685             wxFAIL_MSG( "invalid wxLocaleCategory" ); 
1689         case wxLOCALE_DECIMAL_POINT
: 
1690             if ( cat 
== wxLOCALE_CAT_NUMBER 
) 
1691                 return lc
->decimal_point
; 
1692             else if ( cat 
== wxLOCALE_CAT_MONEY 
) 
1693                 return lc
->mon_decimal_point
; 
1695             wxFAIL_MSG( "invalid wxLocaleCategory" ); 
1698         case wxLOCALE_SHORT_DATE_FMT
: 
1699         case wxLOCALE_LONG_DATE_FMT
: 
1700         case wxLOCALE_DATE_TIME_FMT
: 
1701         case wxLOCALE_TIME_FMT
: 
1702             if ( cat 
!= wxLOCALE_CAT_DATE 
&& cat 
!= wxLOCALE_CAT_DEFAULT 
) 
1704                 wxFAIL_MSG( "invalid wxLocaleCategory" ); 
1708             return GetDateFormatFromLangInfo(index
); 
1712             wxFAIL_MSG( "unknown wxLocaleInfo value" ); 
1720 // ---------------------------------------------------------------------------- 
1721 // global functions and variables 
1722 // ---------------------------------------------------------------------------- 
1724 // retrieve/change current locale 
1725 // ------------------------------ 
1727 // the current locale object 
1728 static wxLocale 
*g_pLocale 
= NULL
; 
1730 wxLocale 
*wxGetLocale() 
1735 wxLocale 
*wxSetLocale(wxLocale 
*pLocale
) 
1737     wxLocale 
*pOld 
= g_pLocale
; 
1738     g_pLocale 
= pLocale
; 
1744 // ---------------------------------------------------------------------------- 
1745 // wxLocale module (for lazy destruction of languagesDB) 
1746 // ---------------------------------------------------------------------------- 
1748 class wxLocaleModule
: public wxModule
 
1750     DECLARE_DYNAMIC_CLASS(wxLocaleModule
) 
1761             wxLocale::DestroyLanguagesDB(); 
1765 IMPLEMENT_DYNAMIC_CLASS(wxLocaleModule
, wxModule
) 
1767 #endif // wxUSE_INTL