]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/intl.cpp
Fix wxHtmlHelpData::SetTempDir() to behave correctly without trailing slash.
[wxWidgets.git] / src / common / intl.cpp
index d27fc4899d0c29586a618b91fcf5b1ab30eaa630..f8b011a91750393dcb0d58e8033b595e46f2f970 100644 (file)
@@ -5,7 +5,6 @@
 // Modified by: Michael N. Filippov <michael@idisys.iae.nsk.su>
 //              (2003/09/30 - PluralForms support)
 // Created:     29/01/98
-// RCS-ID:      $Id$
 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
 // Licence:     wxWindows licence
 /////////////////////////////////////////////////////////////////////////////
 // constants
 // ----------------------------------------------------------------------------
 
-// the constants describing the format of ll_CC locale string
-static const size_t LEN_LANG = 2;
-static const size_t LEN_SUBLANG = 2;
-static const size_t LEN_FULL = LEN_LANG + 1 + LEN_SUBLANG; // 1 for '_'
-
 #define TRACE_I18N wxS("i18n")
 
 // ============================================================================
@@ -99,10 +93,10 @@ static wxLocale *wxSetLocale(wxLocale *pLocale);
 namespace
 {
 
-// get just the language part
+// get just the language part ("en" in "en_GB")
 inline wxString ExtractLang(const wxString& langFull)
 {
-    return langFull.Left(LEN_LANG);
+    return langFull.BeforeFirst('_');
 }
 
 // helper functions of GetSystemLanguage()
@@ -111,7 +105,11 @@ inline wxString ExtractLang(const wxString& langFull)
 // get everything else (including the leading '_')
 inline wxString ExtractNotLang(const wxString& langFull)
 {
-    return langFull.Mid(LEN_LANG);
+    size_t pos = langFull.find('_');
+    if ( pos != wxString::npos )
+        return langFull.substr(pos);
+    else
+        return wxString();
 }
 
 #endif // __UNIX__
@@ -122,7 +120,7 @@ inline wxString ExtractNotLang(const wxString& langFull)
 // wxLanguageInfo
 // ----------------------------------------------------------------------------
 
-#ifdef __WXMSW__
+#ifdef __WINDOWS__
 
 // helper used by wxLanguageInfo::GetLocaleName() and elsewhere to determine
 // whether the locale is Unicode-only (it is if this function returns empty
@@ -178,7 +176,7 @@ wxString wxLanguageInfo::GetLocaleName() const
     return locale;
 }
 
-#endif // __WXMSW__
+#endif // __WINDOWS__
 
 // ----------------------------------------------------------------------------
 // wxLocale
@@ -201,14 +199,17 @@ wxLanguageInfoArray *wxLocale::ms_languagesDB = NULL;
 
 /*static*/ void wxLocale::DestroyLanguagesDB()
 {
-    delete ms_languagesDB;
-    ms_languagesDB = NULL;
+    wxDELETE(ms_languagesDB);
 }
 
 
 void wxLocale::DoCommonInit()
 {
-    m_pszOldLocale = NULL;
+    // Store the current locale in order to be able to restore it in the dtor.
+    m_pszOldLocale = wxSetlocale(LC_ALL, NULL);
+    if ( m_pszOldLocale )
+        m_pszOldLocale = wxStrdup(m_pszOldLocale);
+
 
     m_pOldLocale = wxSetLocale(this);
 
@@ -240,7 +241,7 @@ bool wxLocale::Init(const wxString& name,
                     const wxString& locale,
                     bool            bLoadDefault
 #if WXWIN_COMPATIBILITY_2_8
-                   ,bool            bConvertEncoding
+                   ,bool            WXUNUSED_UNLESS_DEBUG(bConvertEncoding)
 #endif
                     )
 {
@@ -287,15 +288,9 @@ bool wxLocale::DoInit(const wxString& name,
                     wxS("no locale to set in wxLocale::Init()") );
     }
 
-    const char *oldLocale = wxSetlocale(LC_ALL, szLocale);
-    if ( oldLocale )
-        m_pszOldLocale = wxStrdup(oldLocale);
-    else
-        m_pszOldLocale = NULL;
-
-    if ( m_pszOldLocale == NULL )
+    if ( !wxSetlocale(LC_ALL, szLocale) )
     {
-        wxLogError(_("locale '%s' can not be set."), szLocale);
+        wxLogError(_("locale '%s' cannot be set."), szLocale);
     }
 
     // the short name will be used to look for catalog files as well,
@@ -572,6 +567,20 @@ bool wxLocale::Init(int language, int flags)
 #endif // !WX_NO_LOCALE_SUPPORT
 }
 
+namespace
+{
+
+#ifndef __WXOSX__
+// Small helper function: get the value of the given environment variable and
+// return true only if the variable was found and has non-empty value.
+inline bool wxGetNonEmptyEnvVar(const wxString& name, wxString* value)
+{
+    return wxGetEnv(name, value) && !value->empty();
+}
+#endif
+
+} // anonymous namespace
+
 /*static*/ int wxLocale::GetSystemLanguage()
 {
     CreateLanguagesDB();
@@ -583,7 +592,7 @@ bool wxLocale::Init(int language, int flags)
 #if defined(__UNIX__)
     // first get the string identifying the language from the environment
     wxString langFull;
-#ifdef __WXMAC__
+#ifdef __WXOSX__
     wxCFRef<CFLocaleRef> userLocaleRef(CFLocaleCopyCurrent());
 
     // because the locale identifier (kCFLocaleIdentifier) is formatted a little bit differently, eg
@@ -594,9 +603,9 @@ bool wxLocale::Init(int language, int flags)
     str.reset(wxCFRetain((CFStringRef)CFLocaleGetValue(userLocaleRef, kCFLocaleCountryCode)));
     langFull += str.AsString();
 #else
-    if (!wxGetEnv(wxS("LC_ALL"), &langFull) &&
-        !wxGetEnv(wxS("LC_MESSAGES"), &langFull) &&
-        !wxGetEnv(wxS("LANG"), &langFull))
+    if (!wxGetNonEmptyEnvVar(wxS("LC_ALL"), &langFull) &&
+        !wxGetNonEmptyEnvVar(wxS("LC_MESSAGES"), &langFull) &&
+        !wxGetNonEmptyEnvVar(wxS("LANG"), &langFull))
     {
         // no language specified, treat it as English
         return wxLANGUAGE_ENGLISH_US;
@@ -642,94 +651,92 @@ bool wxLocale::Init(int language, int flags)
         langFull.Truncate(posEndLang);
     }
 
-    // in addition to the format above, we also can have full language names
-    // in LANG env var - for example, SuSE is known to use LANG="german" - so
-    // check for this
-
     // do we have just the language (or sublang too)?
-    bool justLang = langFull.length() == LEN_LANG;
-    if ( justLang ||
-        (langFull.length() == LEN_FULL && langFull[LEN_LANG] == wxS('_')) )
-    {
-        // 0. Make sure the lang is according to latest ISO 639
-        //    (this is necessary because glibc uses iw and in instead
-        //    of he and id respectively).
-
-        // the language itself (second part is the dialect/sublang)
-        wxString langOrig = ExtractLang(langFull);
-
-        wxString lang;
-        if ( langOrig == wxS("iw"))
-            lang = wxS("he");
-        else if (langOrig == wxS("in"))
-            lang = wxS("id");
-        else if (langOrig == wxS("ji"))
-            lang = wxS("yi");
-        else if (langOrig == wxS("no_NO"))
-            lang = wxS("nb_NO");
-        else if (langOrig == wxS("no_NY"))
-            lang = wxS("nn_NO");
-        else if (langOrig == wxS("no"))
-            lang = wxS("nb_NO");
-        else
-            lang = langOrig;
+    const bool justLang = langFull.find('_') == wxString::npos;
+
+    // 0. Make sure the lang is according to latest ISO 639
+    //    (this is necessary because glibc uses iw and in instead
+    //    of he and id respectively).
+
+    // the language itself (second part is the dialect/sublang)
+    wxString langOrig = ExtractLang(langFull);
+
+    wxString lang;
+    if ( langOrig == wxS("iw"))
+        lang = wxS("he");
+    else if (langOrig == wxS("in"))
+        lang = wxS("id");
+    else if (langOrig == wxS("ji"))
+        lang = wxS("yi");
+    else if (langOrig == wxS("no_NO"))
+        lang = wxS("nb_NO");
+    else if (langOrig == wxS("no_NY"))
+        lang = wxS("nn_NO");
+    else if (langOrig == wxS("no"))
+        lang = wxS("nb_NO");
+    else
+        lang = langOrig;
 
-        // did we change it?
-        if ( lang != langOrig )
-        {
-            langFull = lang + ExtractNotLang(langFull);
-        }
+    // did we change it?
+    if ( lang != langOrig )
+    {
+        langFull = lang + ExtractNotLang(langFull);
+    }
 
-        // 1. Try to find the language either as is:
-        // a) With modifier if set
-        if ( !modifier.empty() )
+    // 1. Try to find the language either as is:
+    // a) With modifier if set
+    if ( !modifier.empty() )
+    {
+        wxString langFullWithModifier = langFull + modifier;
+        for ( i = 0; i < count; i++ )
         {
-            wxString langFullWithModifier = langFull + modifier;
-            for ( i = 0; i < count; i++ )
-            {
-                if ( ms_languagesDB->Item(i).CanonicalName == langFullWithModifier )
-                    break;
-            }
+            if ( ms_languagesDB->Item(i).CanonicalName == langFullWithModifier )
+                break;
         }
+    }
 
-        // b) Without modifier
-        if ( modifier.empty() || i == count )
+    // b) Without modifier
+    if ( modifier.empty() || i == count )
+    {
+        for ( i = 0; i < count; i++ )
         {
-            for ( i = 0; i < count; i++ )
-            {
-                if ( ms_languagesDB->Item(i).CanonicalName == langFull )
-                    break;
-            }
+            if ( ms_languagesDB->Item(i).CanonicalName == langFull )
+                break;
         }
+    }
 
-        // 2. If langFull is of the form xx_YY, try to find xx:
-        if ( i == count && !justLang )
+    // 2. If langFull is of the form xx_YY, try to find xx:
+    if ( i == count && !justLang )
+    {
+        for ( i = 0; i < count; i++ )
         {
-            for ( i = 0; i < count; i++ )
+            if ( ms_languagesDB->Item(i).CanonicalName == lang )
             {
-                if ( ms_languagesDB->Item(i).CanonicalName == lang )
-                {
-                    break;
-                }
+                break;
             }
         }
+    }
 
-        // 3. If langFull is of the form xx, try to find any xx_YY record:
-        if ( i == count && justLang )
+    // 3. If langFull is of the form xx, try to find any xx_YY record:
+    if ( i == count && justLang )
+    {
+        for ( i = 0; i < count; i++ )
         {
-            for ( i = 0; i < count; i++ )
+            if ( ExtractLang(ms_languagesDB->Item(i).CanonicalName)
+                    == langFull )
             {
-                if ( ExtractLang(ms_languagesDB->Item(i).CanonicalName)
-                        == langFull )
-                {
-                    break;
-                }
+                break;
             }
         }
     }
-    else // not standard format
+
+
+    if ( i == count )
     {
-        // try to find the name in verbose description
+        // In addition to the format above, we also can have full language
+        // names in LANG env var - for example, SuSE is known to use
+        // LANG="german" - so check for use of non-standard format and try to
+        // find the name in verbose description.
         for ( i = 0; i < count; i++ )
         {
             if (ms_languagesDB->Item(i).Description.CmpNoCase(langFull) == 0)
@@ -785,8 +792,9 @@ wxString wxLocale::GetSystemEncodingName()
     UINT codepage = ::GetACP();
     encname.Printf(wxS("windows-%u"), codepage);
 #elif defined(__WXMAC__)
-    // default is just empty string, this resolves to the default system
-    // encoding later
+    encname = wxCFStringRef::AsString(
+                CFStringGetNameOfEncoding(CFStringGetSystemEncoding())
+              );
 #elif defined(__UNIX_LIKE__)
 
 #if defined(HAVE_LANGINFO_H) && defined(CODESET)
@@ -1028,7 +1036,7 @@ wxLocale::~wxLocale()
     wxSetLocale(m_pOldLocale);
 
     wxSetlocale(LC_ALL, m_pszOldLocale);
-    free((wxChar *)m_pszOldLocale);     // const_cast
+    free(const_cast<char *>(m_pszOldLocale));
 }
 
 
@@ -1037,7 +1045,14 @@ wxLocale::~wxLocale()
 bool wxLocale::IsAvailable(int lang)
 {
     const wxLanguageInfo *info = wxLocale::GetLanguageInfo(lang);
-    wxCHECK_MSG( info, false, wxS("invalid language") );
+    if ( !info )
+    {
+        // The language is unknown (this normally only happens when we're
+        // passed wxLANGUAGE_DEFAULT), so we can't support it.
+        wxASSERT_MSG( lang == wxLANGUAGE_DEFAULT,
+                      wxS("No info for a valid language?") );
+        return false;
+    }
 
 #if defined(__WIN32__)
     if ( !info->WinLang )
@@ -1049,16 +1064,21 @@ bool wxLocale::IsAvailable(int lang)
 #elif defined(__UNIX__)
 
     // Test if setting the locale works, then set it back.
-    const char *oldLocale = wxSetlocaleTryUTF8(LC_ALL, info->CanonicalName);
-    if ( !oldLocale )
-    {
-        // Some C libraries don't like xx_YY form and require xx only
-        oldLocale = wxSetlocaleTryUTF8(LC_ALL, ExtractLang(info->CanonicalName));
-        if ( !oldLocale )
-            return false;
-    }
+    char * const oldLocale = wxStrdupA(setlocale(LC_ALL, NULL));
+
+    // Some platforms don't like xx_YY form and require xx only so test for
+    // it too.
+    const bool
+        available = wxSetlocaleTryUTF8(LC_ALL, info->CanonicalName) ||
+                    wxSetlocaleTryUTF8(LC_ALL, ExtractLang(info->CanonicalName));
+
     // restore the original locale
     wxSetlocale(LC_ALL, oldLocale);
+
+    free(oldLocale);
+
+    if ( !available )
+        return false;
 #endif
 
     return true;
@@ -1118,7 +1138,7 @@ wxString wxLocale::GetHeaderValue(const wxString& header,
 // accessors for locale-dependent data
 // ----------------------------------------------------------------------------
 
-#if defined(__WXMSW__) || defined(__WXOSX__)
+#if defined(__WINDOWS__) || defined(__WXOSX__)
 
 namespace
 {
@@ -1140,7 +1160,7 @@ static wxString TranslateFromUnicodeFormat(const wxString& fmt)
 
     const char* formatchars =
         "dghHmMsSy"
-#ifdef __WXMSW__
+#ifdef __WINDOWS__
         "t"
 #else
         "EawD"
@@ -1180,7 +1200,7 @@ static wxString TranslateFromUnicodeFormat(const wxString& fmt)
                             // between 1 and 2 digits for days
                             fmtWX += "%d";
                             break;
-#ifdef __WXMSW__
+#ifdef __WINDOWS__
                         case 3: // ddd
                             fmtWX += "%a";
                             break;
@@ -1193,7 +1213,7 @@ static wxString TranslateFromUnicodeFormat(const wxString& fmt)
                             wxFAIL_MSG( "too many 'd's" );
                     }
                     break;
-#ifndef __WXMSW__
+#ifndef __WINDOWS__
                 case 'D':
                     switch ( lastCount )
                     {
@@ -1336,12 +1356,12 @@ static wxString TranslateFromUnicodeFormat(const wxString& fmt)
                     wxASSERT_MSG( lastCount <= 2, "too many 'g's" );
 
                     break;
-#ifndef __WXMSW__
+#ifndef __WINDOWS__
                 case 'a':
                     fmtWX += "%p";
                     break;
 #endif
-#ifdef __WXMSW__
+#ifdef __WINDOWS__
                 case 't':
                     switch ( lastCount )
                     {
@@ -1381,9 +1401,9 @@ static wxString TranslateFromUnicodeFormat(const wxString& fmt)
 
 } // anonymous namespace
 
-#endif // __WXMSW__ || __WXOSX__
+#endif // __WINDOWS__ || __WXOSX__
 
-#if defined(__WXMSW__)
+#if defined(__WINDOWS__)
 
 namespace
 {
@@ -1411,17 +1431,56 @@ LCTYPE GetLCTYPEFormatFromLocalInfo(wxLocaleInfo index)
 } // anonymous namespace
 
 /* static */
-wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat))
+wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory cat)
 {
-    wxUint32 lcid = LOCALE_USER_DEFAULT;
-    if ( wxGetLocale() )
+    const wxLanguageInfo * const
+        info = wxGetLocale() ? GetLanguageInfo(wxGetLocale()->GetLanguage())
+                             : NULL;
+    if ( !info )
     {
-        const wxLanguageInfo * const
-            info = GetLanguageInfo(wxGetLocale()->GetLanguage());
-        if ( info )
-            lcid = info->GetLCID();
+        // wxSetLocale() hadn't been called yet of failed, hence CRT must be
+        // using "C" locale -- but check it to detect bugs that would happen if
+        // this were not the case.
+        wxASSERT_MSG( strcmp(setlocale(LC_ALL, NULL), "C") == 0,
+                      wxS("You probably called setlocale() directly instead ")
+                      wxS("of using wxLocale and now there is a ")
+                      wxS("mismatch between C/C++ and Windows locale.\n")
+                      wxS("Things are going to break, please only change ")
+                      wxS("locale by creating wxLocale objects to avoid this!") );
+
+
+        // Return the hard coded values for C locale. This is really the right
+        // thing to do as there is no LCID we can use in the code below in this
+        // case, even LOCALE_INVARIANT is not quite the same as C locale (the
+        // only difference is that it uses %Y instead of %y in the date format
+        // but this difference is significant enough).
+        switch ( index )
+        {
+            case wxLOCALE_THOUSANDS_SEP:
+                return wxString();
+
+            case wxLOCALE_DECIMAL_POINT:
+                return ".";
+
+            case wxLOCALE_SHORT_DATE_FMT:
+                return "%m/%d/%y";
+
+            case wxLOCALE_LONG_DATE_FMT:
+                return "%A, %B %d, %Y";
+
+            case wxLOCALE_TIME_FMT:
+                return "%H:%M:%S";
+
+            case wxLOCALE_DATE_TIME_FMT:
+                return "%m/%d/%y %H:%M:%S";
+
+            default:
+                wxFAIL_MSG( "unknown wxLocaleInfo" );
+        }
     }
 
+    const wxUint32 lcid = info->GetLCID();
+
     wxString str;
 
     wxChar buf[256];
@@ -1429,9 +1488,35 @@ wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat))
 
     switch ( index )
     {
+        case wxLOCALE_THOUSANDS_SEP:
+            if ( ::GetLocaleInfo(lcid, LOCALE_STHOUSAND, buf, WXSIZEOF(buf)) )
+                str = buf;
+            break;
+
         case wxLOCALE_DECIMAL_POINT:
-            if ( ::GetLocaleInfo(lcid, LOCALE_SDECIMAL, buf, WXSIZEOF(buf)) )
+            if ( ::GetLocaleInfo(lcid,
+                                 cat == wxLOCALE_CAT_MONEY
+                                     ? LOCALE_SMONDECIMALSEP
+                                     : LOCALE_SDECIMAL,
+                                 buf,
+                                 WXSIZEOF(buf)) )
+            {
                 str = buf;
+
+                // As we get our decimal point separator from Win32 and not the
+                // CRT there is a possibility of mismatch between them and this
+                // can easily happen if the user code called setlocale()
+                // instead of using wxLocale to change the locale. And this can
+                // result in very strange bugs elsewhere in the code as the
+                // assumptions that formatted strings do use the decimal
+                // separator actually fail, so check for it here.
+                wxASSERT_MSG
+                (
+                    wxString::Format("%.3f", 1.23).find(str) != wxString::npos,
+                    "Decimal separator mismatch -- did you use setlocale()?"
+                    "If so, use wxLocale to change the locale instead."
+                );
+            }
             break;
 
         case wxLOCALE_SHORT_DATE_FMT:
@@ -1548,7 +1633,7 @@ wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat))
     return str.AsString();
 }
 
-#else // !__WXMSW__ && !__WXOSX__, assume generic POSIX
+#else // !__WINDOWS__ && !__WXOSX__, assume generic POSIX
 
 namespace
 {