X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/4b6a582bef796b43ad4cf0a96bd40bfd631460c6..c437b3f4e47b74715a2f2385d4862972babd7802:/src/common/intl.cpp diff --git a/src/common/intl.cpp b/src/common/intl.cpp index 023d7cf2ac..0960dfc57b 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -72,9 +72,10 @@ #include "wx/hashset.h" #include "wx/filesys.h" -#if defined(__DARWIN__) +#if defined(__WXOSX__) #include "wx/osx/core/cfref.h" #include + #include #include "wx/osx/core/cfstring.h" #endif @@ -841,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 // ---------------------------------------------------------------------------- @@ -1464,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; @@ -2516,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 @@ -2593,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)) @@ -2674,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 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