]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/intl.cpp
Do not use Tooltips if they are disabled
[wxWidgets.git] / src / common / intl.cpp
index d0a11eac2f7ce58beb5092ffe0e2af015e988c28..0960dfc57b9fbfb6007e3964d28522a1c7495455 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
@@ -869,7 +842,7 @@ wxPluralFormsCalculator* wxPluralFormsCalculator::make(const char* s)
 //
 // 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: 
+//       the GNU gettext manual:
 //       http://www.gnu.org/software/autoconf/manual/gettext/MO-Files.html
 // ----------------------------------------------------------------------------
 
@@ -959,7 +932,7 @@ private:
 
     bool m_bSwapped;   // wrong endianness?
 
-    DECLARE_NO_COPY_CLASS(wxMsgCatalogFile)
+    wxDECLARE_NO_COPY_CLASS(wxMsgCatalogFile);
 };
 
 
@@ -1210,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"));
@@ -1500,9 +1465,9 @@ bool wxMsgCatalogFile::FillHash(wxMessagesHash& hash,
             }
 
             // skip this string
-            // IMPORTANT: accesses to the 'data' pointer are valid only for 
+            // 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() 
+            //            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;
@@ -2442,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);
@@ -2559,13 +2517,12 @@ bool wxLocale::IsAvailable(int lang)
 #elif defined(__UNIX__)
 
     // Test if setting the locale works, then set it back.
-    const char *oldLocale = wxSetlocale(LC_ALL, "");
-    const char *tmp = wxSetlocaleTryUTF8(LC_ALL, info->CanonicalName);
-    if ( !tmp )
+    const char *oldLocale = wxSetlocaleTryUTF8(LC_ALL, info->CanonicalName);
+    if ( !oldLocale )
     {
         // Some C libraries don't like xx_YY form and require xx only
-        tmp = wxSetlocaleTryUTF8(LC_ALL, ExtractLang(info->CanonicalName));
-        if ( !tmp )
+        oldLocale = wxSetlocaleTryUTF8(LC_ALL, ExtractLang(info->CanonicalName));
+        if ( !oldLocale )
             return false;
     }
     // restore the original locale
@@ -2636,56 +2593,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))
@@ -2717,51 +2978,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