]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/intl.cpp
remove "item.cchTextMax=0" line from SetItem() as it doesn't seem to make any sense...
[wxWidgets.git] / src / common / intl.cpp
index e20715c4c75f842604ee1c65c3da87b5a88f683e..ce5a0adfbf81b953ee13809420c956459adeef3a 100644 (file)
 #include "wx/tokenzr.h"
 #include "wx/fontmap.h"
 #include "wx/encconv.h"
-#include "wx/ptr_scpd.h"
+#include "wx/scopedptr.h"
 #include "wx/apptrait.h"
 #include "wx/stdpaths.h"
 #include "wx/hashset.h"
 #include "wx/filesys.h"
 
-#if defined(__DARWIN__)
+#if defined(__WXOSX__)
     #include "wx/osx/core/cfref.h"
     #include <CoreFoundation/CFLocale.h>
+    #include <CoreFoundation/CFDateFormatter.h>
     #include "wx/osx/core/cfstring.h"
 #endif
 
@@ -105,34 +106,6 @@ static const size_t LEN_FULL = LEN_LANG + 1 + LEN_SUBLANG; // 1 for '_'
 // global functions
 // ----------------------------------------------------------------------------
 
-#ifdef __WXDEBUG__
-
-// small class to suppress the translation erros until exit from current scope
-class NoTransErr
-{
-public:
-    NoTransErr() { ms_suppressCount++; }
-    ~NoTransErr() { ms_suppressCount--;  }
-
-    static bool Suppress() { return ms_suppressCount > 0; }
-
-private:
-    static size_t ms_suppressCount;
-};
-
-size_t NoTransErr::ms_suppressCount = 0;
-
-#else // !Debug
-
-class NoTransErr
-{
-public:
-    NoTransErr() { }
-~NoTransErr() { }
-};
-
-#endif // Debug/!Debug
-
 static wxLocale *wxSetLocale(wxLocale *pLocale);
 
 namespace
@@ -868,6 +841,9 @@ wxPluralFormsCalculator* wxPluralFormsCalculator::make(const char* s)
 // wxMsgCatalogFile corresponds to one disk-file message catalog.
 //
 // This is a "low-level" class and is used only by wxMsgCatalog
+// NOTE: for the documentation of the binary catalog (.MO) files refer to
+//       the GNU gettext manual:
+//       http://www.gnu.org/software/autoconf/manual/gettext/MO-Files.html
 // ----------------------------------------------------------------------------
 
 WX_DECLARE_EXPORTED_STRING_HASH_MAP(wxString, wxMessagesHash);
@@ -881,12 +857,12 @@ public:
 
     // load the catalog from disk (szDirPrefix corresponds to language)
     bool Load(const wxString& szDirPrefix, const wxString& szName,
-            wxPluralFormsCalculatorPtr& rPluralFormsCalculator);
+              wxPluralFormsCalculatorPtr& rPluralFormsCalculator);
 
     // fills the hash with string-translation pairs
-    void FillHash(wxMessagesHash& hash,
-                const wxString& msgIdCharset,
-                bool convertEncoding) const;
+    bool FillHash(wxMessagesHash& hash,
+                  const wxString& msgIdCharset,
+                  bool convertEncoding) const;
 
     // return the charset of the strings in this catalog or empty string if
     // none/unknown
@@ -956,7 +932,7 @@ private:
 
     bool m_bSwapped;   // wrong endianness?
 
-    DECLARE_NO_COPY_CLASS(wxMsgCatalogFile)
+    wxDECLARE_NO_COPY_CLASS(wxMsgCatalogFile);
 };
 
 
@@ -1207,16 +1183,8 @@ bool wxMsgCatalogFile::Load(const wxString& szDirPrefix, const wxString& szName,
                     << GetFullSearchPath(ExtractLang(szDirPrefix));
     }
 
-    // don't give translation errors here because the wxstd catalog might
-    // not yet be loaded (and it's normal)
-    //
-    // (we're using an object because we have several return paths)
-
-    NoTransErr noTransErr;
-    wxLogVerbose(_("looking for catalog '%s' in path '%s'."),
-                szName, searchPath.c_str());
-    wxLogTrace(TRACE_I18N, wxS("Looking for \"%s.mo\" in \"%s\""),
-                szName, searchPath.c_str());
+    wxLogTrace(TRACE_I18N, wxS("Looking for \"%s.mo\" in search path \"%s\""),
+                szName, searchPath);
 
     wxFileName fn(szName);
     fn.SetExt(wxS("mo"));
@@ -1304,59 +1272,63 @@ bool wxMsgCatalogFile::Load(const wxString& szDirPrefix, const wxString& szName,
     // plural forms formula from it:
 
     const char* headerData = StringAtOfs(m_pOrigTable, 0);
-    if (headerData && headerData[0] == 0)
+    if ( headerData && headerData[0] == '\0' )
     {
         // Extract the charset:
-        wxString header = wxString::FromAscii(StringAtOfs(m_pTransTable, 0));
-        int begin = header.Find(wxS("Content-Type: text/plain; charset="));
-        if (begin != wxNOT_FOUND)
+        const char * const header = StringAtOfs(m_pTransTable, 0);
+        const char *
+            cset = strstr(header, "Content-Type: text/plain; charset=");
+        if ( cset )
         {
-            begin += 34; //strlen("Content-Type: text/plain; charset=")
-            size_t end = header.find('\n', begin);
-            if (end != size_t(-1))
+            cset += 34; // strlen("Content-Type: text/plain; charset=")
+
+            const char * const csetEnd = strchr(cset, '\n');
+            if ( csetEnd )
             {
-                m_charset.assign(header, begin, end - begin);
-                if (m_charset == wxS("CHARSET"))
+                m_charset = wxString(cset, csetEnd - cset);
+                if ( m_charset == wxS("CHARSET") )
                 {
                     // "CHARSET" is not valid charset, but lazy translator
-                    m_charset.Clear();
+                    m_charset.empty();
                 }
             }
         }
         // else: incorrectly filled Content-Type header
 
         // Extract plural forms:
-        begin = header.Find(wxS("Plural-Forms:"));
-        if (begin != wxNOT_FOUND)
+        const char * plurals = strstr(header, "Plural-Forms:");
+        if ( plurals )
         {
-            begin += 13;
-            size_t end = header.find('\n', begin);
-            if (end != size_t(-1))
+            plurals += 13; // strlen("Plural-Forms:")
+            const char * const pluralsEnd = strchr(plurals, '\n');
+            if ( pluralsEnd )
             {
-                wxString pfs(header, begin, end - begin);
-                wxPluralFormsCalculator* pCalculator = wxPluralFormsCalculator
-                    ::make(pfs.ToAscii());
-                if (pCalculator != 0)
+                const size_t pluralsLen = pluralsEnd - plurals;
+                wxCharBuffer buf(pluralsLen);
+                strncpy(buf.data(), plurals, pluralsLen);
+                wxPluralFormsCalculator * const
+                    pCalculator = wxPluralFormsCalculator::make(buf);
+                if ( pCalculator )
                 {
                     rPluralFormsCalculator.reset(pCalculator);
                 }
                 else
                 {
-                    wxLogVerbose(_("Cannot parse Plural-Forms:'%s'"), pfs.c_str());
+                    wxLogVerbose(_("Failed to parse Plural-Forms: '%s'"),
+                                 buf.data());
                 }
             }
         }
-        if (rPluralFormsCalculator.get() == NULL)
-        {
+
+        if ( !rPluralFormsCalculator.get() )
             rPluralFormsCalculator.reset(wxPluralFormsCalculator::make());
-        }
     }
 
     // everything is fine
     return true;
 }
 
-void wxMsgCatalogFile::FillHash(wxMessagesHash& hash,
+bool wxMsgCatalogFile::FillHash(wxMessagesHash& hash,
                                 const wxString& msgIdCharset,
                                 bool convertEncoding) const
 {
@@ -1444,6 +1416,8 @@ void wxMsgCatalogFile::FillHash(wxMessagesHash& hash,
     for (size_t32 i = 0; i < m_numStrings; i++)
     {
         const char *data = StringAtOfs(m_pOrigTable, i);
+        if (!data)
+            return false; // may happen for invalid MO files
 
         wxString msgid;
 #if wxUSE_UNICODE
@@ -1458,6 +1432,9 @@ void wxMsgCatalogFile::FillHash(wxMessagesHash& hash,
 #endif // wxUSE_UNICODE
 
         data = StringAtOfs(m_pTransTable, i);
+        if (!data)
+            return false; // may happen for invalid MO files
+
         size_t length = Swap(m_pTransTable[i].nLen);
         size_t offset = 0;
         size_t index = 0;
@@ -1488,7 +1465,12 @@ void wxMsgCatalogFile::FillHash(wxMessagesHash& hash,
             }
 
             // skip this string
-            offset += strlen(str) + 1;
+            // IMPORTANT: accesses to the 'data' pointer are valid only for
+            //            the first 'length+1' bytes (GNU specs says that the
+            //            final NUL is not counted in length); using wxStrnlen()
+            //            we make sure we don't access memory beyond the valid range
+            //            (which otherwise may happen for invalid MO files):
+            offset += wxStrnlen(str, length - offset) + 1;
             ++index;
         }
     }
@@ -1497,6 +1479,8 @@ void wxMsgCatalogFile::FillHash(wxMessagesHash& hash,
     delete sourceConv;
     delete inputConvPtr;
 #endif // wxUSE_WCHAR_T
+
+    return true;
 }
 
 
@@ -1531,7 +1515,8 @@ bool wxMsgCatalog::Load(const wxString& dirPrefix, const wxString& name,
     if ( !file.Load(dirPrefix, name, m_pluralFormsCalculator) )
         return false;
 
-    file.FillHash(m_messages, msgIdCharset, bConvertEncoding);
+    if ( !file.FillHash(m_messages, msgIdCharset, bConvertEncoding) )
+        return false;
 
 #if !wxUSE_UNICODE
     // we should use a conversion compatible with the message catalog encoding
@@ -2180,7 +2165,7 @@ wxString wxLocale::GetSystemEncodingName()
         // the environment variables (in most cases this won't work, but I was
         // out of ideas)
         char *lang = getenv( "LC_ALL");
-        char *dot = lang ? strchr(lang, '.') : (char *)NULL;
+        char *dot = lang ? strchr(lang, '.') : NULL;
         if (!dot)
         {
             lang = getenv( "LC_CTYPE" );
@@ -2422,18 +2407,11 @@ const wxString& wxLocale::GetString(const wxString& origString,
 
     if ( trans == NULL )
     {
-#ifdef __WXDEBUG__
-        if ( !NoTransErr::Suppress() )
-        {
-            NoTransErr noTransErr;
-
-            wxLogTrace(TRACE_I18N,
-                    wxS("string \"%s\"[%ld] not found in %slocale '%s'."),
-                    origString, (long)n,
-                    wxString::Format(wxS("domain '%s' "), domain).c_str(),
-                    m_strLocale.c_str());
-        }
-#endif // __WXDEBUG__
+        wxLogTrace(TRACE_I18N,
+                wxS("string \"%s\"[%ld] not found in %slocale '%s'."),
+                origString, (long)n,
+                wxString::Format(wxS("domain '%s' "), domain).c_str(),
+                m_strLocale.c_str());
 
         if (n == size_t(-1))
             return GetUntranslatedString(origString);
@@ -2616,56 +2594,360 @@ bool wxLocale::AddCatalog(const wxString& szDomain,
 // accessors for locale-dependent data
 // ----------------------------------------------------------------------------
 
+#if defined(__WXMSW__) || defined(__WXOSX__)
+
+namespace
+{
+
+// This function translates from Unicode date formats described at
+//
+//      http://unicode.org/reports/tr35/tr35-6.html#Date_Format_Patterns
+//
+// to strftime()-like syntax. This translation is not lossless but we try to do
+// our best.
+
+static wxString TranslateFromUnicodeFormat(const wxString& fmt)
+{
+    wxString fmtWX;
+    fmtWX.reserve(fmt.length());
+
+    char chLast = '\0';
+    size_t lastCount = 0;
+    
+    const char* formatchars = 
+        "dghHmMsSy"
+#ifdef __WXMSW__
+        "t"
+#else
+        "EawD"
+#endif
+        ;
+    for ( wxString::const_iterator p = fmt.begin(); /* end handled inside */; ++p )
+    {
+        if ( p != fmt.end() )
+        {
+            if ( *p == chLast )
+            {
+                lastCount++;
+                continue;
+            }
+
+            const wxUniChar ch = (*p).GetValue();
+            if ( ch.IsAscii() && strchr(formatchars, ch) )
+            {
+                // these characters come in groups, start counting them
+                chLast = ch;
+                lastCount = 1;
+                continue;
+            }
+        }
+
+        // interpret any special characters we collected so far
+        if ( lastCount )
+        {
+            switch ( chLast )
+            {
+                case 'd':
+                    switch ( lastCount )
+                    {
+                        case 1: // d
+                        case 2: // dd
+                            // these two are the same as we don't distinguish
+                            // between 1 and 2 digits for days
+                            fmtWX += "%d";
+                            break;
+#ifdef __WXMSW__
+                        case 3: // ddd
+                            fmtWX += "%a";
+                            break;
+
+                        case 4: // dddd
+                            fmtWX += "%A";
+                            break;
+#endif
+                        default:
+                            wxFAIL_MSG( "too many 'd's" );
+                    }
+                    break;
+#ifndef __WXMSW__
+                case 'D':
+                    switch ( lastCount )
+                    {
+                        case 1: // D
+                        case 2: // DD
+                        case 3: // DDD
+                            fmtWX += "%j";
+                            break;
+
+                        default:
+                            wxFAIL_MSG( "wrong number of 'D's" );
+                    }
+                    break;
+               case 'w':
+                    switch ( lastCount )
+                    {
+                        case 1: // w
+                        case 2: // ww
+                            fmtWX += "%W";
+                            break;
+
+                        default:
+                            wxFAIL_MSG( "wrong number of 'w's" );
+                    }
+                    break;
+                case 'E':
+                   switch ( lastCount )
+                    {
+                        case 1: // E
+                        case 2: // EE
+                        case 3: // EEE
+                            fmtWX += "%a";
+                            break;
+                        case 4: // EEEE
+                            fmtWX += "%A";
+                            break;
+                        case 5: // EEEEE
+                            fmtWX += "%a";
+                            break;
+
+                        default:
+                            wxFAIL_MSG( "wrong number of 'E's" );
+                    }
+                    break;
+#endif
+                case 'M':
+                    switch ( lastCount )
+                    {
+                        case 1: // M
+                        case 2: // MM
+                            // as for 'd' and 'dd' above
+                            fmtWX += "%m";
+                            break;
+
+                        case 3:
+                            fmtWX += "%b";
+                            break;
+
+                        case 4:
+                            fmtWX += "%B";
+                            break;
+
+                        default:
+                            wxFAIL_MSG( "too many 'M's" );
+                    }
+                    break;
+
+                case 'y':
+                    switch ( lastCount )
+                    {
+                        case 1: // y
+                        case 2: // yy
+                            fmtWX += "%y";
+                            break;
+
+                        case 4: // yyyy
+                            fmtWX += "%Y";
+                            break;
+
+                        default:
+                            wxFAIL_MSG( "wrong number of 'y's" );
+                    }
+                    break;
+
+                case 'H':
+                    switch ( lastCount )
+                    {
+                        case 1: // H
+                        case 2: // HH
+                            fmtWX += "%H";
+                            break;
+
+                        default:
+                            wxFAIL_MSG( "wrong number of 'H's" );
+                    }
+                    break;
+
+               case 'h':
+                    switch ( lastCount )
+                    {
+                        case 1: // h
+                        case 2: // hh
+                            fmtWX += "%I";
+                            break;
+
+                        default:
+                            wxFAIL_MSG( "wrong number of 'h's" );
+                    }
+                    break;
+
+               case 'm':
+                    switch ( lastCount )
+                    {
+                        case 1: // m
+                        case 2: // mm
+                            fmtWX += "%M";
+                            break;
+
+                        default:
+                            wxFAIL_MSG( "wrong number of 'm's" );
+                    }
+                    break;
+
+               case 's':
+                    switch ( lastCount )
+                    {
+                        case 1: // s
+                        case 2: // ss
+                            fmtWX += "%S";
+                            break;
+
+                        default:
+                            wxFAIL_MSG( "wrong number of 's's" );
+                    }
+                    break;
+
+                case 'g':
+                    // strftime() doesn't have era string,
+                    // ignore this format
+                    wxASSERT_MSG( lastCount <= 2, "too many 'g's" );
+         
+                    break;
+#ifndef __WXMSW__
+                case 'a':
+                    fmtWX += "%p";
+                    break;
+#endif
+#ifdef __WXMSW__
+                case 't':
+                    switch ( lastCount )
+                    {
+                        case 1: // t
+                        case 2: // tt
+                            fmtWX += "%p";
+                            break;
+
+                        default:
+                            wxFAIL_MSG( "too many 't's" );
+                    }
+                    break;
+#endif
+                default:
+                    wxFAIL_MSG( "unreachable" );
+            }
+
+            chLast = '\0';
+            lastCount = 0;
+        }
+
+        if ( p == fmt.end() )
+            break;
+
+        // not a special character so must be just a separator, treat as is
+        if ( *p == _T('%') )
+        {
+            // this one needs to be escaped
+            fmtWX += _T('%');
+        }
+
+        fmtWX += *p;
+    }
+
+    return fmtWX;
+}
+
+} // anonymous namespace
+
+#endif // __WXMSW__ || __WXOSX__
+
 #if defined(__WXMSW__)
 
+namespace
+{
+
+LCTYPE GetLCTYPEFormatFromLocalInfo(wxLocaleInfo index)
+{
+    switch ( index )
+    {
+        case wxLOCALE_SHORT_DATE_FMT:
+            return LOCALE_SSHORTDATE;
+
+        case wxLOCALE_LONG_DATE_FMT:
+            return LOCALE_SLONGDATE;
+
+        case wxLOCALE_TIME_FMT:
+            return LOCALE_STIMEFORMAT;
+
+        default:
+            wxFAIL_MSG( "no matching LCTYPE" );
+    }
+
+    return 0;
+}
+
+} // anonymous namespace
+
 /* static */
 wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat))
 {
     wxUint32 lcid = LOCALE_USER_DEFAULT;
-
-    if (wxGetLocale())
+    if ( wxGetLocale() )
     {
-        const wxLanguageInfo *info = GetLanguageInfo(wxGetLocale()->GetLanguage());
+        const wxLanguageInfo * const
+            info = GetLanguageInfo(wxGetLocale()->GetLanguage());
         if ( info )
             lcid = info->GetLCID();
     }
 
     wxString str;
-    wxChar buffer[256];
-    size_t count;
-    buffer[0] = wxS('\0');
-    switch (index)
+
+    wxChar buf[256];
+    buf[0] = wxT('\0');
+
+    switch ( index )
     {
         case wxLOCALE_DECIMAL_POINT:
-            count = ::GetLocaleInfo(lcid, LOCALE_SDECIMAL, buffer, 256);
-            if (!count)
-                str << wxS(".");
-            else
-                str << buffer;
+            if ( ::GetLocaleInfo(lcid, LOCALE_SDECIMAL, buf, WXSIZEOF(buf)) )
+                str = buf;
             break;
-#if 0
-        case wxSYS_LIST_SEPARATOR:
-            count = ::GetLocaleInfo(lcid, LOCALE_SLIST, buffer, 256);
-            if (!count)
-                str << wxS(",");
-            else
-                str << buffer;
+
+        case wxLOCALE_SHORT_DATE_FMT:
+        case wxLOCALE_LONG_DATE_FMT:
+        case wxLOCALE_TIME_FMT:
+            if ( ::GetLocaleInfo(lcid, GetLCTYPEFormatFromLocalInfo(index),
+                                 buf, WXSIZEOF(buf)) )
+            {
+                return TranslateFromUnicodeFormat(buf);
+            }
             break;
-        case wxSYS_LEADING_ZERO: // 0 means no leading zero, 1 means leading zero
-            count = ::GetLocaleInfo(lcid, LOCALE_ILZERO, buffer, 256);
-            if (!count)
-                str << wxS("0");
-            else
-                str << buffer;
+
+        case wxLOCALE_DATE_TIME_FMT:
+            // there doesn't seem to be any specific setting for this, so just
+            // combine date and time ones
+            //
+            // we use the short date because this is what "%c" uses by default
+            // ("%#c" uses long date but we have no way to specify the
+            // alternate representation here)
+            {
+                const wxString datefmt = GetInfo(wxLOCALE_SHORT_DATE_FMT);
+                if ( datefmt.empty() )
+                    break;
+
+                const wxString timefmt = GetInfo(wxLOCALE_TIME_FMT);
+                if ( timefmt.empty() )
+                    break;
+
+                str << datefmt << ' ' << timefmt;
+            }
             break;
-#endif
+
         default:
-            wxFAIL_MSG(wxS("Unknown System String !"));
+            wxFAIL_MSG( "unknown wxLocaleInfo" );
     }
+
     return str;
 }
 
-#elif defined(__DARWIN__)
+#elif defined(__WXOSX__)
 
 /* static */
 wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat))
@@ -2697,51 +2979,180 @@ wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat))
             cfstr = (CFStringRef) CFLocaleGetValue(userLocaleRef, kCFLocaleDecimalSeparator);
             break;
 
+        case wxLOCALE_SHORT_DATE_FMT:
+        case wxLOCALE_LONG_DATE_FMT:
+        case wxLOCALE_DATE_TIME_FMT:
+        case wxLOCALE_TIME_FMT:
+            {
+                CFDateFormatterStyle dateStyle = kCFDateFormatterNoStyle;
+                CFDateFormatterStyle timeStyle = kCFDateFormatterNoStyle;
+                switch (index )
+                {
+                    case wxLOCALE_SHORT_DATE_FMT:
+                        dateStyle = kCFDateFormatterShortStyle;
+                        break;
+                    case wxLOCALE_LONG_DATE_FMT:
+                        dateStyle = kCFDateFormatterFullStyle;
+                        break;
+                    case wxLOCALE_DATE_TIME_FMT:
+                        dateStyle = kCFDateFormatterFullStyle;
+                        timeStyle = kCFDateFormatterMediumStyle;
+                        break;
+                    case wxLOCALE_TIME_FMT:
+                        timeStyle = kCFDateFormatterMediumStyle;
+                        break;
+                    default:
+                        wxFAIL_MSG( "unexpected time locale" );
+                        return wxString();
+                }
+                wxCFRef<CFDateFormatterRef> dateFormatter( CFDateFormatterCreate
+                    (NULL, userLocaleRef, dateStyle, timeStyle));
+                wxCFStringRef cfs = wxCFRetain( CFDateFormatterGetFormat(dateFormatter ));
+                wxString format = TranslateFromUnicodeFormat(cfs.AsString());
+                // we always want full years
+                format.Replace("%y","%Y");
+                return format;
+            }
+            break;
+
         default:
             wxFAIL_MSG( "Unknown locale info" );
-            cfstr = CFSTR("");
-            break;
+            return wxString();
     }
 
     wxCFStringRef str(wxCFRetain(cfstr));
     return str.AsString();
 }
 
-#else // !__WXMSW__ && !__DARWIN__
+#else // !__WXMSW__ && !__WXOSX__, assume generic POSIX
+
+namespace
+{
+
+wxString GetDateFormatFromLangInfo(wxLocaleInfo index)
+{
+#ifdef HAVE_LANGINFO_H
+    // array containing parameters for nl_langinfo() indexes by offset of index
+    // from wxLOCALE_SHORT_DATE_FMT
+    static const nl_item items[] =
+    {
+        D_FMT, D_T_FMT, D_T_FMT, T_FMT,
+    };
+
+    const int nlidx = index - wxLOCALE_SHORT_DATE_FMT;
+    if ( nlidx < 0 || nlidx >= (int)WXSIZEOF(items) )
+    {
+        wxFAIL_MSG( "logic error in GetInfo() code" );
+        return wxString();
+    }
+
+    const wxString fmt(nl_langinfo(items[nlidx]));
+
+    // just return the format returned by nl_langinfo() except for long date
+    // format which we need to recover from date/time format ourselves (but not
+    // if we failed completely)
+    if ( fmt.empty() || index != wxLOCALE_LONG_DATE_FMT )
+        return fmt;
+
+    // this is not 100% precise but the idea is that a typical date/time format
+    // under POSIX systems is a combination of a long date format with time one
+    // so we should be able to get just the long date format by removing all
+    // time-specific format specifiers
+    static const char *timeFmtSpecs = "HIklMpPrRsSTXzZ";
+    static const char *timeSep = " :./-";
+
+    wxString fmtDateOnly;
+    const wxString::const_iterator end = fmt.end();
+    wxString::const_iterator lastSep = end;
+    for ( wxString::const_iterator p = fmt.begin(); p != end; ++p )
+    {
+        if ( strchr(timeSep, *p) )
+        {
+            if ( lastSep == end )
+                lastSep = p;
+
+            // skip it for now, we'll discard it if it's followed by a time
+            // specifier later or add it to fmtDateOnly if it is not
+            continue;
+        }
+
+        if ( *p == '%' &&
+                (p + 1 != end) && strchr(timeFmtSpecs, p[1]) )
+        {
+            // time specified found: skip it and any preceding separators
+            ++p;
+            lastSep = end;
+            continue;
+        }
+
+        if ( lastSep != end )
+        {
+            fmtDateOnly += wxString(lastSep, p);
+            lastSep = end;
+        }
+
+        fmtDateOnly += *p;
+    }
+
+    return fmtDateOnly;
+#else // !HAVE_LANGINFO_H
+    // no fallback, let the application deal with unavailability of
+    // nl_langinfo() itself as there is no good way for us to do it (well, we
+    // could try to reverse engineer the format from strftime() output but this
+    // looks like too much trouble considering the relatively small number of
+    // systems without nl_langinfo() still in use)
+    return wxString();
+#endif // HAVE_LANGINFO_H/!HAVE_LANGINFO_H
+}
+
+} // anonymous namespace
 
 /* static */
 wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory cat)
 {
-    struct lconv *locale_info = localeconv();
-    switch (cat)
+    lconv * const lc = localeconv();
+    if ( !lc )
+        return wxString();
+
+    switch ( index )
     {
-        case wxLOCALE_CAT_NUMBER:
-            switch (index)
-            {
-                case wxLOCALE_THOUSANDS_SEP:
-                    return wxString(locale_info->thousands_sep,
-                                    *wxConvCurrent);
-                case wxLOCALE_DECIMAL_POINT:
-                    return wxString(locale_info->decimal_point,
-                                    *wxConvCurrent);
-                default:
-                    return wxEmptyString;
-            }
-        case wxLOCALE_CAT_MONEY:
-            switch (index)
+        case wxLOCALE_THOUSANDS_SEP:
+            if ( cat == wxLOCALE_CAT_NUMBER )
+                return lc->thousands_sep;
+            else if ( cat == wxLOCALE_CAT_MONEY )
+                return lc->mon_thousands_sep;
+
+            wxFAIL_MSG( "invalid wxLocaleCategory" );
+            break;
+
+
+        case wxLOCALE_DECIMAL_POINT:
+            if ( cat == wxLOCALE_CAT_NUMBER )
+                return lc->decimal_point;
+            else if ( cat == wxLOCALE_CAT_MONEY )
+                return lc->mon_decimal_point;
+
+            wxFAIL_MSG( "invalid wxLocaleCategory" );
+            break;
+
+        case wxLOCALE_SHORT_DATE_FMT:
+        case wxLOCALE_LONG_DATE_FMT:
+        case wxLOCALE_DATE_TIME_FMT:
+        case wxLOCALE_TIME_FMT:
+            if ( cat != wxLOCALE_CAT_DATE && cat != wxLOCALE_CAT_DEFAULT )
             {
-                case wxLOCALE_THOUSANDS_SEP:
-                    return wxString(locale_info->mon_thousands_sep,
-                                    *wxConvCurrent);
-                case wxLOCALE_DECIMAL_POINT:
-                    return wxString(locale_info->mon_decimal_point,
-                                    *wxConvCurrent);
-                default:
-                    return wxEmptyString;
+                wxFAIL_MSG( "invalid wxLocaleCategory" );
+                break;
             }
+
+            return GetDateFormatFromLangInfo(index);
+
+
         default:
-            return wxEmptyString;
+            wxFAIL_MSG( "unknown wxLocaleInfo value" );
     }
+
+    return wxString();
 }
 
 #endif // platform