X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/071ccf2a7cf494fb404e41808dc31bf5df46701a..bc6ca1a9e68a84817092856059d8ee9f5d109d1d:/src/common/intl.cpp diff --git a/src/common/intl.cpp b/src/common/intl.cpp index dfc8eb3ad3..ff680307af 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -66,20 +66,17 @@ #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(__WXMAC__) - #include "wx/mac/private.h" // includes mac headers -#endif - -#if defined(__DARWIN__) - #include "wx/mac/corefoundation/cfref.h" +#if defined(__WXOSX__) + #include "wx/osx/core/cfref.h" #include - #include "wx/mac/corefoundation/cfstring.h" + #include + #include "wx/osx/core/cfstring.h" #endif // ---------------------------------------------------------------------------- @@ -98,7 +95,7 @@ typedef wxUint32 size_t32; const size_t32 MSGCATALOG_MAGIC = 0x950412de; const size_t32 MSGCATALOG_MAGIC_SW = 0xde120495; -// the constants describing the format of lang_LANG locale string +// 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 '_' @@ -109,53 +106,29 @@ 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); -// helper functions of GetSystemLanguage() -#ifdef __UNIX__ +namespace +{ // get just the language part -static inline wxString ExtractLang(const wxString& langFull) +inline wxString ExtractLang(const wxString& langFull) { return langFull.Left(LEN_LANG); } +// helper functions of GetSystemLanguage() +#ifdef __UNIX__ + // get everything else (including the leading '_') -static inline wxString ExtractNotLang(const wxString& langFull) +inline wxString ExtractNotLang(const wxString& langFull) { return langFull.Mid(LEN_LANG); } #endif // __UNIX__ +} // anonymous namespace // ---------------------------------------------------------------------------- // Plural forms parser @@ -240,7 +213,7 @@ wxPluralFormsScanner::wxPluralFormsScanner(const char* s) : m_s(s) bool wxPluralFormsScanner::nextToken() { wxPluralFormsToken::Type type = wxPluralFormsToken::T_ERROR; - while (isspace(*m_s)) + while (isspace((unsigned char) *m_s)) { ++m_s; } @@ -248,20 +221,20 @@ bool wxPluralFormsScanner::nextToken() { type = wxPluralFormsToken::T_EOF; } - else if (isdigit(*m_s)) + else if (isdigit((unsigned char) *m_s)) { wxPluralFormsToken::Number number = *m_s++ - '0'; - while (isdigit(*m_s)) + while (isdigit((unsigned char) *m_s)) { number = number * 10 + (*m_s++ - '0'); } m_token.setNumber(number); type = wxPluralFormsToken::T_NUMBER; } - else if (isalpha(*m_s)) + else if (isalpha((unsigned char) *m_s)) { const char* begin = m_s++; - while (isalnum(*m_s)) + while (isalnum((unsigned char) *m_s)) { ++m_s; } @@ -505,7 +478,7 @@ private: wxDEFINE_SCOPED_PTR_TYPE(wxPluralFormsCalculator) void wxPluralFormsCalculator::init(wxPluralFormsToken::Number nplurals, - wxPluralFormsNode* plural) + wxPluralFormsNode* plural) { m_nplurals = nplurals; m_plural.reset(plural); @@ -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); @@ -877,14 +853,14 @@ class wxMsgCatalogFile public: // ctor & dtor wxMsgCatalogFile(); - ~wxMsgCatalogFile(); + ~wxMsgCatalogFile(); // load the catalog from disk (szDirPrefix corresponds to language) bool Load(const wxString& szDirPrefix, const wxString& szName, wxPluralFormsCalculatorPtr& rPluralFormsCalculator); // fills the hash with string-translation pairs - void FillHash(wxMessagesHash& hash, + bool FillHash(wxMessagesHash& hash, const wxString& msgIdCharset, bool convertEncoding) const; @@ -898,20 +874,20 @@ private: // an entry in the string table struct wxMsgTableEntry { - size_t32 nLen; // length of the string - size_t32 ofsString; // pointer to the string + size_t32 nLen; // length of the string + size_t32 ofsString; // pointer to the string }; // header of a .mo file struct wxMsgCatalogHeader { - size_t32 magic, // offset +00: magic id - revision, // +04: revision - numStrings; // +08: number of strings in the file - size_t32 ofsOrigTable, // +0C: start of original string table - ofsTransTable; // +10: start of translated string table - size_t32 nHashSize, // +14: hash table size - ofsHashTable; // +18: offset of hash table start + size_t32 magic, // offset +00: magic id + revision, // +04: revision + numStrings; // +08: number of strings in the file + size_t32 ofsOrigTable, // +0C: start of original string table + ofsTransTable; // +10: start of translated string table + size_t32 nHashSize, // +14: hash table size + ofsHashTable; // +18: offset of hash table start }; // all data is stored here @@ -928,8 +904,8 @@ private: // swap the 2 halves of 32 bit integer if needed size_t32 Swap(size_t32 ui) const { - return m_bSwapped ? (ui << 24) | ((ui & 0xff00) << 8) | - ((ui >> 8) & 0xff00) | (ui >> 24) + return m_bSwapped ? (ui << 24) | ((ui & 0xff00) << 8) | + ((ui >> 8) & 0xff00) | (ui >> 24) : ui; } @@ -937,7 +913,7 @@ private: // facilitate doing pointer arithmetic with it char *StringData() const { - return wx_static_cast(char *, m_data.GetData()); + return static_cast(m_data.GetData()); } const char *StringAtOfs(wxMsgTableEntry *pTable, size_t32 n) const @@ -956,7 +932,7 @@ private: bool m_bSwapped; // wrong endianness? - DECLARE_NO_COPY_CLASS(wxMsgCatalogFile) + wxDECLARE_NO_COPY_CLASS(wxMsgCatalogFile); }; @@ -977,7 +953,7 @@ public: // load the catalog from disk (szDirPrefix corresponds to language) bool Load(const wxString& dirPrefix, const wxString& name, - const wxString& msgIdCharset, bool bConvertEncoding = false); + const wxString& msgIdCharset, bool bConvertEncoding = false); // get name of the catalog wxString GetName() const { return m_name; } @@ -1027,9 +1003,9 @@ static wxString wxGetANSICodePageForLocale(LCID lcid) wxChar buffer[16]; if ( ::GetLocaleInfo(lcid, LOCALE_IDEFAULTANSICODEPAGE, - buffer, WXSIZEOF(buffer)) > 0 ) + buffer, WXSIZEOF(buffer)) > 0 ) { - if ( buffer[0] != _T('0') || buffer[1] != _T('\0') ) + if ( buffer[0] != wxT('0') || buffer[1] != wxT('\0') ) cp = buffer; //else: this locale doesn't use ANSI code page } @@ -1049,24 +1025,24 @@ wxString wxLanguageInfo::GetLocaleName() const const LCID lcid = GetLCID(); wxChar buffer[256]; - buffer[0] = _T('\0'); + buffer[0] = wxT('\0'); if ( !::GetLocaleInfo(lcid, LOCALE_SENGLANGUAGE, buffer, WXSIZEOF(buffer)) ) { - wxLogLastError(_T("GetLocaleInfo(LOCALE_SENGLANGUAGE)")); + wxLogLastError(wxT("GetLocaleInfo(LOCALE_SENGLANGUAGE)")); return locale; } locale << buffer; if ( ::GetLocaleInfo(lcid, LOCALE_SENGCOUNTRY, - buffer, WXSIZEOF(buffer)) > 0 ) + buffer, WXSIZEOF(buffer)) > 0 ) { - locale << _T('_') << buffer; + locale << wxT('_') << buffer; } const wxString cp = wxGetANSICodePageForLocale(lcid); if ( !cp.empty() ) { - locale << _T('.') << cp; + locale << wxT('.') << cp; } return locale; @@ -1107,8 +1083,8 @@ wxString GetMsgCatalogSubdirs(const wxString& prefix, const wxString& lang) wxString searchPath; searchPath.reserve(4*pathPrefix.length()); searchPath << pathPrefix << wxFILE_SEP_PATH << "LC_MESSAGES" << wxPATH_SEP - << prefix << wxFILE_SEP_PATH << wxPATH_SEP - << pathPrefix; + << prefix << wxFILE_SEP_PATH << wxPATH_SEP + << pathPrefix; return searchPath; } @@ -1120,7 +1096,7 @@ static wxString GetFullSearchPath(const wxString& lang) wxArrayString paths; paths.reserve(gs_searchPrefixes.size() + 1); size_t n, - count = gs_searchPrefixes.size(); + count = gs_searchPrefixes.size(); for ( n = 0; n < count; n++ ) { paths.Add(GetMsgCatalogSubdirs(gs_searchPrefixes[n], lang)); @@ -1177,182 +1153,182 @@ static wxString GetFullSearchPath(const wxString& lang) bool wxMsgCatalogFile::Load(const wxString& szDirPrefix, const wxString& szName, wxPluralFormsCalculatorPtr& rPluralFormsCalculator) { - wxString searchPath; + wxCHECK_MSG( szDirPrefix.length() >= LEN_LANG, false, + "invalid language specification" ); + + wxString searchPath; #if wxUSE_FONTMAP - // first look for the catalog for this language and the current locale: - // notice that we don't use the system name for the locale as this would - // force us to install catalogs in different locations depending on the - // system but always use the canonical name - wxFontEncoding encSys = wxLocale::GetSystemEncoding(); - if ( encSys != wxFONTENCODING_SYSTEM ) - { - wxString fullname(szDirPrefix); - fullname << wxS('.') << wxFontMapperBase::GetEncodingName(encSys); - searchPath << GetFullSearchPath(fullname) << wxPATH_SEP; - } + // first look for the catalog for this language and the current locale: + // notice that we don't use the system name for the locale as this would + // force us to install catalogs in different locations depending on the + // system but always use the canonical name + wxFontEncoding encSys = wxLocale::GetSystemEncoding(); + if ( encSys != wxFONTENCODING_SYSTEM ) + { + wxString fullname(szDirPrefix); + fullname << wxS('.') << wxFontMapperBase::GetEncodingName(encSys); + searchPath << GetFullSearchPath(fullname) << wxPATH_SEP; + } #endif // wxUSE_FONTMAP - searchPath += GetFullSearchPath(szDirPrefix); - size_t sublocaleIndex = szDirPrefix.find(wxS('_')); - if ( sublocaleIndex != wxString::npos ) - { - // also add just base locale name: for things like "fr_BE" (belgium - // french) we should use "fr" if no belgium specific message catalogs - // exist - searchPath << wxPATH_SEP - << GetFullSearchPath(szDirPrefix.Left(sublocaleIndex)); - } - - // 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()); - - wxFileName fn(szName); - fn.SetExt(wxS("mo")); - - wxString strFullName; + searchPath += GetFullSearchPath(szDirPrefix); + if ( szDirPrefix.length() > LEN_LANG && szDirPrefix[LEN_LANG] == wxS('_') ) + { + // also add just base locale name: for things like "fr_BE" (Belgium + // French) we should use fall back on plain "fr" if no Belgium-specific + // message catalogs exist + searchPath << wxPATH_SEP + << GetFullSearchPath(ExtractLang(szDirPrefix)); + } + + wxLogTrace(TRACE_I18N, wxS("Looking for \"%s.mo\" in search path \"%s\""), + szName, searchPath); + + wxFileName fn(szName); + fn.SetExt(wxS("mo")); + + wxString strFullName; #if wxUSE_FILESYSTEM - wxFileSystem fileSys; - if ( !fileSys.FindFileInPath(&strFullName, searchPath, fn.GetFullPath()) ) + wxFileSystem fileSys; + if ( !fileSys.FindFileInPath(&strFullName, searchPath, fn.GetFullPath()) ) #else // !wxUSE_FILESYSTEM - if ( !wxFindFileInPath(&strFullName, searchPath, fn.GetFullPath()) ) + if ( !wxFindFileInPath(&strFullName, searchPath, fn.GetFullPath()) ) #endif // wxUSE_FILESYSTEM/!wxUSE_FILESYSTEM - { - wxLogVerbose(_("catalog file for domain '%s' not found."), szName); - wxLogTrace(TRACE_I18N, wxS("Catalog \"%s.mo\" not found"), szName); - return false; - } + { + wxLogVerbose(_("catalog file for domain '%s' not found."), szName); + wxLogTrace(TRACE_I18N, wxS("Catalog \"%s.mo\" not found"), szName); + return false; + } - // open file and read its data - wxLogVerbose(_("using catalog '%s' from '%s'."), szName, strFullName.c_str()); - wxLogTrace(TRACE_I18N, wxS("Using catalog \"%s\"."), strFullName.c_str()); + // open file and read its data + wxLogVerbose(_("using catalog '%s' from '%s'."), szName, strFullName.c_str()); + wxLogTrace(TRACE_I18N, wxS("Using catalog \"%s\"."), strFullName.c_str()); #if wxUSE_FILESYSTEM - wxFSFile * const fileMsg = fileSys.OpenFile(strFullName); - if ( !fileMsg ) - return false; + wxFSFile * const fileMsg = fileSys.OpenFile(strFullName); + if ( !fileMsg ) + return false; - wxInputStream *fileStream = fileMsg->GetStream(); - m_data.SetDataLen(0); + wxInputStream *fileStream = fileMsg->GetStream(); + m_data.SetDataLen(0); - static const size_t chunkSize = 4096; - while ( !fileStream->Eof() ) { - fileStream->Read(m_data.GetAppendBuf(chunkSize), chunkSize); - m_data.UngetAppendBuf(fileStream->LastRead()); - } + static const size_t chunkSize = 4096; + while ( !fileStream->Eof() ) { + fileStream->Read(m_data.GetAppendBuf(chunkSize), chunkSize); + m_data.UngetAppendBuf(fileStream->LastRead()); + } - delete fileMsg; + delete fileMsg; #else // !wxUSE_FILESYSTEM - wxFile fileMsg(strFullName); - if ( !fileMsg.IsOpened() ) - return false; + wxFile fileMsg(strFullName); + if ( !fileMsg.IsOpened() ) + return false; - // get the file size (assume it is less than 4Gb...) - wxFileOffset lenFile = fileMsg.Length(); - if ( lenFile == wxInvalidOffset ) - return false; + // get the file size (assume it is less than 4Gb...) + wxFileOffset lenFile = fileMsg.Length(); + if ( lenFile == wxInvalidOffset ) + return false; - size_t nSize = wx_truncate_cast(size_t, lenFile); - wxASSERT_MSG( nSize == lenFile + size_t(0), wxS("message catalog bigger than 4GB?") ); + size_t nSize = wx_truncate_cast(size_t, lenFile); + wxASSERT_MSG( nSize == lenFile + size_t(0), wxS("message catalog bigger than 4GB?") ); - // read the whole file in memory - if ( fileMsg.Read(m_data.GetWriteBuf(nSize), nSize) != lenFile ) - return false; + // read the whole file in memory + if ( fileMsg.Read(m_data.GetWriteBuf(nSize), nSize) != lenFile ) + return false; + + m_data.UngetWriteBuf(nSize); #endif // wxUSE_FILESYSTEM/!wxUSE_FILESYSTEM - // examine header - bool bValid = m_data.GetDataLen() > sizeof(wxMsgCatalogHeader); + // examine header + bool bValid = m_data.GetDataLen() > sizeof(wxMsgCatalogHeader); - const wxMsgCatalogHeader *pHeader = (wxMsgCatalogHeader *)m_data.GetData(); - if ( bValid ) { - // we'll have to swap all the integers if it's true - m_bSwapped = pHeader->magic == MSGCATALOG_MAGIC_SW; + const wxMsgCatalogHeader *pHeader = (wxMsgCatalogHeader *)m_data.GetData(); + if ( bValid ) { + // we'll have to swap all the integers if it's true + m_bSwapped = pHeader->magic == MSGCATALOG_MAGIC_SW; - // check the magic number - bValid = m_bSwapped || pHeader->magic == MSGCATALOG_MAGIC; - } + // check the magic number + bValid = m_bSwapped || pHeader->magic == MSGCATALOG_MAGIC; + } - if ( !bValid ) { - // it's either too short or has incorrect magic number - wxLogWarning(_("'%s' is not a valid message catalog."), strFullName.c_str()); + if ( !bValid ) { + // it's either too short or has incorrect magic number + wxLogWarning(_("'%s' is not a valid message catalog."), strFullName.c_str()); - return false; - } - - // initialize - m_numStrings = Swap(pHeader->numStrings); - m_pOrigTable = (wxMsgTableEntry *)(StringData() + - Swap(pHeader->ofsOrigTable)); - m_pTransTable = (wxMsgTableEntry *)(StringData() + - Swap(pHeader->ofsTransTable)); - - // now parse catalog's header and try to extract catalog charset and - // plural forms formula from it: - - const char* headerData = StringAtOfs(m_pOrigTable, 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) - { - begin += 34; //strlen("Content-Type: text/plain; charset=") - size_t end = header.find('\n', begin); - if (end != size_t(-1)) - { - m_charset.assign(header, begin, end - begin); - if (m_charset == wxS("CHARSET")) - { - // "CHARSET" is not valid charset, but lazy translator - m_charset.Clear(); - } - } - } - // else: incorrectly filled Content-Type header - - // Extract plural forms: - begin = header.Find(wxS("Plural-Forms:")); - if (begin != wxNOT_FOUND) - { - begin += 13; - size_t end = header.find('\n', begin); - if (end != size_t(-1)) - { - wxString pfs(header, begin, end - begin); - wxPluralFormsCalculator* pCalculator = wxPluralFormsCalculator - ::make(pfs.ToAscii()); - if (pCalculator != 0) - { - rPluralFormsCalculator.reset(pCalculator); - } - else - { - wxLogVerbose(_("Cannot parse Plural-Forms:'%s'"), pfs.c_str()); - } - } - } - if (rPluralFormsCalculator.get() == NULL) - { - rPluralFormsCalculator.reset(wxPluralFormsCalculator::make()); - } - } - - // everything is fine - return true; -} - -void wxMsgCatalogFile::FillHash(wxMessagesHash& hash, + return false; + } + + // initialize + m_numStrings = Swap(pHeader->numStrings); + m_pOrigTable = (wxMsgTableEntry *)(StringData() + + Swap(pHeader->ofsOrigTable)); + m_pTransTable = (wxMsgTableEntry *)(StringData() + + Swap(pHeader->ofsTransTable)); + + // now parse catalog's header and try to extract catalog charset and + // plural forms formula from it: + + const char* headerData = StringAtOfs(m_pOrigTable, 0); + if ( headerData && headerData[0] == '\0' ) + { + // Extract the charset: + const char * const header = StringAtOfs(m_pTransTable, 0); + const char * + cset = strstr(header, "Content-Type: text/plain; charset="); + if ( cset ) + { + cset += 34; // strlen("Content-Type: text/plain; charset=") + + const char * const csetEnd = strchr(cset, '\n'); + if ( csetEnd ) + { + m_charset = wxString(cset, csetEnd - cset); + if ( m_charset == wxS("CHARSET") ) + { + // "CHARSET" is not valid charset, but lazy translator + m_charset.empty(); + } + } + } + // else: incorrectly filled Content-Type header + + // Extract plural forms: + const char * plurals = strstr(header, "Plural-Forms:"); + if ( plurals ) + { + plurals += 13; // strlen("Plural-Forms:") + const char * const pluralsEnd = strchr(plurals, '\n'); + if ( pluralsEnd ) + { + 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(_("Failed to parse Plural-Forms: '%s'"), + buf.data()); + } + } + } + + if ( !rPluralFormsCalculator.get() ) + rPluralFormsCalculator.reset(wxPluralFormsCalculator::make()); + } + + // everything is fine + return true; +} + +bool wxMsgCatalogFile::FillHash(wxMessagesHash& hash, const wxString& msgIdCharset, bool convertEncoding) const { @@ -1376,7 +1352,7 @@ void wxMsgCatalogFile::FillHash(wxMessagesHash& hash, #if wxUSE_WCHAR_T // conversion to use to convert catalog strings to the GUI encoding wxMBConv *inputConv, - *inputConvPtr = NULL; // same as inputConv but safely deleteable + *inputConvPtr = NULL; // same as inputConv but safely deleteable if ( convertEncoding && !m_charset.empty() ) { inputConvPtr = @@ -1402,7 +1378,7 @@ void wxMsgCatalogFile::FillHash(wxMessagesHash& hash, #elif wxUSE_FONTMAP wxASSERT_MSG( msgIdCharset.empty(), - wxS("non-ASCII msgid languages only supported if wxUSE_WCHAR_T=1") ); + wxS("non-ASCII msgid languages only supported if wxUSE_WCHAR_T=1") ); wxEncodingConverter converter; if ( convertEncoding ) @@ -1440,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 @@ -1454,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; @@ -1484,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; } } @@ -1493,6 +1479,8 @@ void wxMsgCatalogFile::FillHash(wxMessagesHash& hash, delete sourceConv; delete inputConvPtr; #endif // wxUSE_WCHAR_T + + return true; } @@ -1527,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 @@ -1603,13 +1592,13 @@ wxLanguageInfoArray *wxLocale::ms_languagesDB = NULL; void wxLocale::DoCommonInit() { - m_pszOldLocale = NULL; + m_pszOldLocale = NULL; - m_pOldLocale = wxSetLocale(this); + m_pOldLocale = wxSetLocale(this); - m_pMsgCat = NULL; - m_language = wxLANGUAGE_UNKNOWN; - m_initialized = false; + m_pMsgCat = NULL; + m_language = wxLANGUAGE_UNKNOWN; + m_initialized = false; } // NB: this function has (desired) side effect of changing current locale @@ -1619,68 +1608,70 @@ bool wxLocale::Init(const wxString& name, bool bLoadDefault, bool bConvertEncoding) { - wxASSERT_MSG( !m_initialized, - wxS("you can't call wxLocale::Init more than once") ); + wxASSERT_MSG( !m_initialized, + wxS("you can't call wxLocale::Init more than once") ); - m_initialized = true; - m_strLocale = name; - m_strShort = shortName; - m_bConvertEncoding = bConvertEncoding; - m_language = wxLANGUAGE_UNKNOWN; + m_initialized = true; + m_strLocale = name; + m_strShort = shortName; + m_bConvertEncoding = bConvertEncoding; + m_language = wxLANGUAGE_UNKNOWN; - // change current locale (default: same as long name) - wxString szLocale(locale); - if ( szLocale.empty() ) - { - // the argument to setlocale() - szLocale = shortName; - - wxCHECK_MSG( !szLocale.empty(), false, - wxS("no locale to set in wxLocale::Init()") ); - } + // change current locale (default: same as long name) + wxString szLocale(locale); + if ( szLocale.empty() ) + { + // the argument to setlocale() + szLocale = shortName; - const char *oldLocale = wxSetlocale(LC_ALL, szLocale); - if ( oldLocale ) - m_pszOldLocale = wxStrdup(oldLocale); - else - m_pszOldLocale = NULL; + wxCHECK_MSG( !szLocale.empty(), false, + wxS("no locale to set in wxLocale::Init()") ); + } - if ( m_pszOldLocale == NULL ) - wxLogError(_("locale '%s' can not be set."), szLocale); + const char *oldLocale = wxSetlocale(LC_ALL, szLocale); + if ( oldLocale ) + m_pszOldLocale = wxStrdup(oldLocale); + else + m_pszOldLocale = NULL; - // the short name will be used to look for catalog files as well, - // so we need something here - if ( m_strShort.empty() ) { - // FIXME I don't know how these 2 letter abbreviations are formed, - // this wild guess is surely wrong - if ( !szLocale.empty() ) + if ( m_pszOldLocale == NULL ) { - m_strShort += (wxChar)wxTolower(szLocale[0]); - if ( szLocale.length() > 1 ) - m_strShort += (wxChar)wxTolower(szLocale[1]); + wxLogError(_("locale '%s' can not be set."), szLocale); } - } - // load the default catalog with wxWidgets standard messages - m_pMsgCat = NULL; - bool bOk = true; - if ( bLoadDefault ) - { - bOk = AddCatalog(wxS("wxstd")); + // the short name will be used to look for catalog files as well, + // so we need something here + if ( m_strShort.empty() ) { + // FIXME I don't know how these 2 letter abbreviations are formed, + // this wild guess is surely wrong + if ( !szLocale.empty() ) + { + m_strShort += (wxChar)wxTolower(szLocale[0]); + if ( szLocale.length() > 1 ) + m_strShort += (wxChar)wxTolower(szLocale[1]); + } + } - // there may be a catalog with toolkit specific overrides, it is not - // an error if this does not exist - if ( bOk ) + // load the default catalog with wxWidgets standard messages + m_pMsgCat = NULL; + bool bOk = true; + if ( bLoadDefault ) { - wxString port(wxPlatformInfo::Get().GetPortIdName()); - if ( !port.empty() ) - { - AddCatalog(port.BeforeFirst(wxS('/')).MakeLower()); - } + bOk = AddCatalog(wxS("wxstd")); + + // there may be a catalog with toolkit specific overrides, it is not + // an error if this does not exist + if ( bOk ) + { + wxString port(wxPlatformInfo::Get().GetPortIdName()); + if ( !port.empty() ) + { + AddCatalog(port.BeforeFirst(wxS('/')).MakeLower()); + } + } } - } - return bOk; + return bOk; } @@ -1739,7 +1730,7 @@ bool wxLocale::Init(int language, int flags) // We failed to detect system language, so we will use English: if (lang == wxLANGUAGE_UNKNOWN) { - return false; + return false; } const wxLanguageInfo *info = GetLanguageInfo(lang); @@ -1764,7 +1755,7 @@ bool wxLocale::Init(int language, int flags) const char *retloc = wxSetlocaleTryUTF8(LC_ALL, locale); - const wxString langOnly = locale.Left(2); + const wxString langOnly = ExtractLang(locale); if ( !retloc ) { // Some C libraries don't like xx_YY form and require xx only @@ -1797,11 +1788,11 @@ bool wxLocale::Init(int language, int flags) // so will translate the abbrev for them wxString localeAlt; if ( langOnly == wxS("he") ) - localeAlt = wxS("iw") + locale.Mid(3); + localeAlt = wxS("iw") + ExtractNotLang(locale); else if ( langOnly == wxS("id") ) - localeAlt = wxS("in") + locale.Mid(3); + localeAlt = wxS("in") + ExtractNotLang(locale); else if ( langOnly == wxS("yi") ) - localeAlt = wxS("ji") + locale.Mid(3); + localeAlt = wxS("ji") + ExtractNotLang(locale); else if ( langOnly == wxS("nb") ) localeAlt = wxS("no_NO"); else if ( langOnly == wxS("nn") ) @@ -1811,7 +1802,7 @@ bool wxLocale::Init(int language, int flags) { retloc = wxSetlocaleTryUTF8(LC_ALL, localeAlt); if ( !retloc ) - retloc = wxSetlocaleTryUTF8(LC_ALL, localeAlt.Left(2)); + retloc = wxSetlocaleTryUTF8(LC_ALL, ExtractLang(localeAlt)); } } @@ -1842,11 +1833,11 @@ bool wxLocale::Init(int language, int flags) } else // language supported by Windows { - const wxUint32 lcid = info->GetLCID(); - // Windows CE doesn't have SetThreadLocale() and there doesn't seem // to be any equivalent #ifndef __WXWINCE__ + const wxUint32 lcid = info->GetLCID(); + // change locale used by Windows functions ::SetThreadLocale(lcid); #endif @@ -1897,7 +1888,7 @@ bool wxLocale::Init(int language, int flags) if ( !retloc ) { // Some C libraries don't like xx_YY form and require xx only - retloc = wxSetlocale(LC_ALL, locale.Mid(0,2)); + retloc = wxSetlocale(LC_ALL, ExtractLang(locale)); } #else wxUnusedVar(flags); @@ -1915,8 +1906,8 @@ bool wxLocale::Init(int language, int flags) } if ( !Init(name, canonical, retloc, - (flags & wxLOCALE_LOAD_DEFAULT) != 0, - (flags & wxLOCALE_CONV_ENCODING) != 0) ) + (flags & wxLOCALE_LOAD_DEFAULT) != 0, + (flags & wxLOCALE_CONV_ENCODING) != 0) ) { ret = false; } @@ -1945,7 +1936,7 @@ void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix) // init i to avoid compiler warning size_t i = 0, - count = ms_languagesDB->GetCount(); + count = ms_languagesDB->GetCount(); #if defined(__UNIX__) // first get the string identifying the language from the environment @@ -1996,8 +1987,13 @@ void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix) // for now we don't use the encoding, although we probably should (doing // translations of the msg catalogs on the fly as required) (TODO) // - // we don't use the modifiers neither but we probably should translate - // "euro" into iso885915 + // we need the modified for languages like Valencian: ca_ES@valencia + // though, remember it + wxString modifier; + size_t posModifier = langFull.find_first_of(wxS("@")); + if ( posModifier != wxString::npos ) + modifier = langFull.Mid(posModifier); + size_t posEndLang = langFull.find_first_of(wxS("@.")); if ( posEndLang != wxString::npos ) { @@ -2011,7 +2007,7 @@ void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix) // 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('_')) ) + (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 @@ -2043,11 +2039,24 @@ void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix) } // 1. Try to find the language either as is: - for ( i = 0; i < count; i++ ) + // a) With modifier if set + if ( !modifier.empty() ) + { + wxString langFullWithModifier = langFull + modifier; + for ( i = 0; i < count; i++ ) + { + if ( ms_languagesDB->Item(i).CanonicalName == langFullWithModifier ) + break; + } + } + + // b) Without modifier + if ( modifier.empty() || i == count ) { - if ( ms_languagesDB->Item(i).CanonicalName == langFull ) + for ( i = 0; i < count; i++ ) { - break; + if ( ms_languagesDB->Item(i).CanonicalName == langFull ) + break; } } @@ -2158,7 +2167,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" ); @@ -2219,7 +2228,7 @@ wxFontEncoding wxLocale::GetSystemEncoding() return wxFONTENCODING_CP950; } #elif defined(__WXMAC__) - TextEncoding encoding = 0 ; + CFStringEncoding encoding = 0 ; encoding = CFStringGetSystemEncoding() ; return wxMacGetFontEncFromSystemEnc( encoding ) ; #elif defined(__UNIX_LIKE__) && wxUSE_FONTMAP @@ -2400,18 +2409,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); @@ -2438,7 +2440,7 @@ const wxString& wxLocale::GetUntranslatedString(const wxString& str) } wxString wxLocale::GetHeaderValue(const wxString& header, - const wxString& domain) const + const wxString& domain) const { if ( header.empty() ) return wxEmptyString; @@ -2468,11 +2470,11 @@ wxString wxLocale::GetHeaderValue(const wxString& header, } if ( !trans || trans->empty() ) - return wxEmptyString; + return wxEmptyString; size_t found = trans->find(header); if ( found == wxString::npos ) - return wxEmptyString; + return wxEmptyString; found += header.length() + 2 /* ': ' */; @@ -2480,7 +2482,7 @@ wxString wxLocale::GetHeaderValue(const wxString& header, size_t endLine = trans->find(wxS('\n'), found); size_t len = (endLine == wxString::npos) ? - wxString::npos : (endLine - found); + wxString::npos : (endLine - found); return trans->substr(found, len); } @@ -2494,7 +2496,7 @@ wxMsgCatalog *wxLocale::FindCatalog(const wxString& domain) const for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext ) { if ( pMsgCat->GetName() == domain ) - return pMsgCat; + return pMsgCat; } return NULL; @@ -2517,13 +2519,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, info->CanonicalName.Left(2)); - if ( !tmp ) + oldLocale = wxSetlocaleTryUTF8(LC_ALL, ExtractLang(info->CanonicalName)); + if ( !oldLocale ) return false; } // restore the original locale @@ -2536,7 +2537,7 @@ bool wxLocale::IsAvailable(int lang) // check if the given catalog is loaded bool wxLocale::IsLoaded(const wxString& szDomain) const { - return FindCatalog(szDomain) != NULL; + return FindCatalog(szDomain) != NULL; } // add a catalog to our linked list @@ -2547,98 +2548,407 @@ bool wxLocale::AddCatalog(const wxString& szDomain) // add a catalog to our linked list bool wxLocale::AddCatalog(const wxString& szDomain, - wxLanguage msgIdLanguage, - const wxString& msgIdCharset) + wxLanguage msgIdLanguage, + const wxString& msgIdCharset) { - wxMsgCatalog *pMsgCat = new wxMsgCatalog; + wxCHECK_MSG( !m_strShort.empty(), false, "must initialize catalog first" ); - if ( pMsgCat->Load(m_strShort, szDomain, msgIdCharset, m_bConvertEncoding) ) { - // add it to the head of the list so that in GetString it will - // be searched before the catalogs added earlier - pMsgCat->m_pNext = m_pMsgCat; - m_pMsgCat = pMsgCat; - - return true; - } - else { - // don't add it because it couldn't be loaded anyway - delete pMsgCat; // It is OK to not load catalog if the msgid language and m_language match, // in which case we can directly display the texts embedded in program's // source code: - if (m_language == msgIdLanguage) + if ( msgIdLanguage == m_language ) return true; + + wxMsgCatalog *pMsgCat = new wxMsgCatalog; + + if ( pMsgCat->Load(m_strShort, szDomain, msgIdCharset, m_bConvertEncoding) ) + { + // add it to the head of the list so that in GetString it will + // be searched before the catalogs added earlier + pMsgCat->m_pNext = m_pMsgCat; + m_pMsgCat = pMsgCat; + + return true; + } + + // don't add it because it couldn't be loaded anyway + delete pMsgCat; + + // If there's no exact match, we may still get partial match where the // (basic) language is same, but the country differs. For example, it's // permitted to use en_US strings from sources even if m_language is en_GB: const wxLanguageInfo *msgIdLangInfo = GetLanguageInfo(msgIdLanguage); if ( msgIdLangInfo && - msgIdLangInfo->CanonicalName.Mid(0, 2) == m_strShort.Mid(0, 2) ) + ExtractLang(msgIdLangInfo->CanonicalName) == ExtractLang(m_strShort) ) { return true; } return false; - } } // ---------------------------------------------------------------------------- // 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 == wxT('%') ) + { + // this one needs to be escaped + fmtWX += wxT('%'); + } + + 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)) @@ -2647,10 +2957,10 @@ wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat)) if ( wxGetLocale() ) { userLocaleRefRaw = CFLocaleCreate - ( + ( kCFAllocatorDefault, wxCFStringRef(wxGetLocale()->GetCanonicalName()) - ); + ); } else // no current locale, use the default one { @@ -2659,61 +2969,191 @@ wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat)) wxCFRef userLocaleRef(userLocaleRefRaw); - CFTypeRef cfstr; + CFStringRef cfstr = 0; switch ( index ) { case wxLOCALE_THOUSANDS_SEP: - cfstr = CFLocaleGetValue(userLocaleRef, kCFLocaleGroupingSeparator); + cfstr = (CFStringRef) CFLocaleGetValue(userLocaleRef, kCFLocaleGroupingSeparator); break; case wxLOCALE_DECIMAL_POINT: - cfstr = CFLocaleGetValue(userLocaleRef, kCFLocaleDecimalSeparator); + 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" ); + return wxString(); } - wxCFStringRef - str(CFStringCreateCopy(NULL, static_cast(cfstr))); + 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 @@ -2730,14 +3170,14 @@ static wxLocale *g_pLocale = NULL; wxLocale *wxGetLocale() { - return g_pLocale; + return g_pLocale; } wxLocale *wxSetLocale(wxLocale *pLocale) { - wxLocale *pOld = g_pLocale; - g_pLocale = pLocale; - return pOld; + wxLocale *pOld = g_pLocale; + g_pLocale = pLocale; + return pOld; } @@ -2764,7 +3204,6 @@ IMPLEMENT_DYNAMIC_CLASS(wxLocaleModule, wxModule) // ---------------------------------------------------------------------------- - // --- --- --- generated code begins here --- --- --- // This table is generated by misc/languages/genlang.py @@ -2938,6 +3377,9 @@ IMPLEMENT_DYNAMIC_CLASS(wxLocaleModule, wxModule) #ifndef LANG_RUSSIAN #define LANG_RUSSIAN (0) #endif +#ifndef LANG_SAMI +#define LANG_SAMI (0) +#endif #ifndef LANG_SANSKRIT #define LANG_SANSKRIT (0) #endif @@ -3262,9 +3704,9 @@ IMPLEMENT_DYNAMIC_CLASS(wxLocaleModule, wxModule) #define LNG(wxlang, canonical, winlang, winsublang, layout, desc) \ info.Language = wxlang; \ - info.CanonicalName = wxS(canonical); \ + info.CanonicalName = wxT(canonical); \ info.LayoutDirection = layout; \ - info.Description = wxS(desc); \ + info.Description = wxT(desc); \ SETWINLANG(info, winlang, winsublang) \ AddLanguage(info); @@ -3298,6 +3740,7 @@ void wxLocale::InitLanguagesDB() LNG(wxLANGUAGE_ARABIC_YEMEN, "ar_YE", LANG_ARABIC , SUBLANG_ARABIC_YEMEN , wxLayout_RightToLeft, "Arabic (Yemen)") LNG(wxLANGUAGE_ARMENIAN, "hy" , LANG_ARMENIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Armenian") LNG(wxLANGUAGE_ASSAMESE, "as" , LANG_ASSAMESE , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Assamese") + LNG(wxLANGUAGE_ASTURIAN, "ast" , 0 , 0 , wxLayout_LeftToRight, "Asturian") LNG(wxLANGUAGE_AYMARA, "ay" , 0 , 0 , wxLayout_LeftToRight, "Aymara") LNG(wxLANGUAGE_AZERI, "az" , LANG_AZERI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Azeri") LNG(wxLANGUAGE_AZERI_CYRILLIC, "az" , LANG_AZERI , SUBLANG_AZERI_CYRILLIC , wxLayout_LeftToRight, "Azeri (Cyrillic)") @@ -3346,7 +3789,7 @@ void wxLocale::InitLanguagesDB() LNG(wxLANGUAGE_ESPERANTO, "eo" , 0 , 0 , wxLayout_LeftToRight, "Esperanto") LNG(wxLANGUAGE_ESTONIAN, "et_EE", LANG_ESTONIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Estonian") LNG(wxLANGUAGE_FAEROESE, "fo_FO", LANG_FAEROESE , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Faeroese") - LNG(wxLANGUAGE_FARSI, "fa_IR", LANG_FARSI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Farsi") + LNG(wxLANGUAGE_FARSI, "fa_IR", LANG_FARSI , SUBLANG_DEFAULT , wxLayout_RightToLeft, "Farsi") LNG(wxLANGUAGE_FIJI, "fj" , 0 , 0 , wxLayout_LeftToRight, "Fiji") LNG(wxLANGUAGE_FINNISH, "fi_FI", LANG_FINNISH , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Finnish") LNG(wxLANGUAGE_FRENCH, "fr_FR", LANG_FRENCH , SUBLANG_FRENCH , wxLayout_LeftToRight, "French") @@ -3357,7 +3800,7 @@ void wxLocale::InitLanguagesDB() LNG(wxLANGUAGE_FRENCH_SWISS, "fr_CH", LANG_FRENCH , SUBLANG_FRENCH_SWISS , wxLayout_LeftToRight, "French (Swiss)") LNG(wxLANGUAGE_FRISIAN, "fy" , 0 , 0 , wxLayout_LeftToRight, "Frisian") LNG(wxLANGUAGE_GALICIAN, "gl_ES", 0 , 0 , wxLayout_LeftToRight, "Galician") - LNG(wxLANGUAGE_GEORGIAN, "ka" , LANG_GEORGIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Georgian") + LNG(wxLANGUAGE_GEORGIAN, "ka_GE", LANG_GEORGIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Georgian") LNG(wxLANGUAGE_GERMAN, "de_DE", LANG_GERMAN , SUBLANG_GERMAN , wxLayout_LeftToRight, "German") LNG(wxLANGUAGE_GERMAN_AUSTRIAN, "de_AT", LANG_GERMAN , SUBLANG_GERMAN_AUSTRIAN , wxLayout_LeftToRight, "German (Austrian)") LNG(wxLANGUAGE_GERMAN_BELGIUM, "de_BE", 0 , 0 , wxLayout_LeftToRight, "German (Belgium)") @@ -3393,7 +3836,7 @@ void wxLocale::InitLanguagesDB() LNG(wxLANGUAGE_KIRUNDI, "rn" , 0 , 0 , wxLayout_LeftToRight, "Kirundi") LNG(wxLANGUAGE_KONKANI, "" , LANG_KONKANI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Konkani") LNG(wxLANGUAGE_KOREAN, "ko_KR", LANG_KOREAN , SUBLANG_KOREAN , wxLayout_LeftToRight, "Korean") - LNG(wxLANGUAGE_KURDISH, "ku" , 0 , 0 , wxLayout_LeftToRight, "Kurdish") + LNG(wxLANGUAGE_KURDISH, "ku_TR", 0 , 0 , wxLayout_LeftToRight, "Kurdish") LNG(wxLANGUAGE_LAOTHIAN, "lo" , 0 , 0 , wxLayout_LeftToRight, "Laothian") LNG(wxLANGUAGE_LATIN, "la" , 0 , 0 , wxLayout_LeftToRight, "Latin") LNG(wxLANGUAGE_LATVIAN, "lv_LV", LANG_LATVIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Latvian") @@ -3412,7 +3855,7 @@ void wxLocale::InitLanguagesDB() LNG(wxLANGUAGE_MOLDAVIAN, "mo" , 0 , 0 , wxLayout_LeftToRight, "Moldavian") LNG(wxLANGUAGE_MONGOLIAN, "mn" , 0 , 0 , wxLayout_LeftToRight, "Mongolian") LNG(wxLANGUAGE_NAURU, "na" , 0 , 0 , wxLayout_LeftToRight, "Nauru") - LNG(wxLANGUAGE_NEPALI, "ne" , LANG_NEPALI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Nepali") + LNG(wxLANGUAGE_NEPALI, "ne_NP", LANG_NEPALI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Nepali") LNG(wxLANGUAGE_NEPALI_INDIA, "ne_IN", LANG_NEPALI , SUBLANG_NEPALI_INDIA , wxLayout_LeftToRight, "Nepali (India)") LNG(wxLANGUAGE_NORWEGIAN_BOKMAL, "nb_NO", LANG_NORWEGIAN , SUBLANG_NORWEGIAN_BOKMAL , wxLayout_LeftToRight, "Norwegian (Bokmal)") LNG(wxLANGUAGE_NORWEGIAN_NYNORSK, "nn_NO", LANG_NORWEGIAN , SUBLANG_NORWEGIAN_NYNORSK , wxLayout_LeftToRight, "Norwegian (Nynorsk)") @@ -3429,12 +3872,16 @@ void wxLocale::InitLanguagesDB() LNG(wxLANGUAGE_ROMANIAN, "ro_RO", LANG_ROMANIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Romanian") LNG(wxLANGUAGE_RUSSIAN, "ru_RU", LANG_RUSSIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Russian") LNG(wxLANGUAGE_RUSSIAN_UKRAINE, "ru_UA", 0 , 0 , wxLayout_LeftToRight, "Russian (Ukraine)") + LNG(wxLANGUAGE_SAMI, "se_NO", LANG_SAMI , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Northern Sami") LNG(wxLANGUAGE_SAMOAN, "sm" , 0 , 0 , wxLayout_LeftToRight, "Samoan") LNG(wxLANGUAGE_SANGHO, "sg" , 0 , 0 , wxLayout_LeftToRight, "Sangho") LNG(wxLANGUAGE_SANSKRIT, "sa" , LANG_SANSKRIT , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Sanskrit") LNG(wxLANGUAGE_SCOTS_GAELIC, "gd" , 0 , 0 , wxLayout_LeftToRight, "Scots Gaelic") + LNG(wxLANGUAGE_SERBIAN, "sr_RS", LANG_SERBIAN , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Serbian") + LNG(wxLANGUAGE_SERBIAN_CYRILLIC, "sr_RS", LANG_SERBIAN , SUBLANG_SERBIAN_CYRILLIC , wxLayout_LeftToRight, "Serbian (Cyrillic)") + LNG(wxLANGUAGE_SERBIAN_LATIN, "sr_RS@latin", LANG_SERBIAN , SUBLANG_SERBIAN_LATIN , wxLayout_LeftToRight, "Serbian (Latin)") LNG(wxLANGUAGE_SERBIAN_CYRILLIC, "sr_YU", LANG_SERBIAN , SUBLANG_SERBIAN_CYRILLIC , wxLayout_LeftToRight, "Serbian (Cyrillic)") - LNG(wxLANGUAGE_SERBIAN_LATIN, "sr_YU", LANG_SERBIAN , SUBLANG_SERBIAN_LATIN , wxLayout_LeftToRight, "Serbian (Latin)") + LNG(wxLANGUAGE_SERBIAN_LATIN, "sr_YU@latin", LANG_SERBIAN , SUBLANG_SERBIAN_LATIN , wxLayout_LeftToRight, "Serbian (Latin)") LNG(wxLANGUAGE_SERBO_CROATIAN, "sh" , 0 , 0 , wxLayout_LeftToRight, "Serbo-Croatian") LNG(wxLANGUAGE_SESOTHO, "st" , 0 , 0 , wxLayout_LeftToRight, "Sesotho") LNG(wxLANGUAGE_SETSWANA, "tn" , 0 , 0 , wxLayout_LeftToRight, "Setswana") @@ -3491,6 +3938,7 @@ void wxLocale::InitLanguagesDB() LNG(wxLANGUAGE_UZBEK, "uz" , LANG_UZBEK , SUBLANG_DEFAULT , wxLayout_LeftToRight, "Uzbek") LNG(wxLANGUAGE_UZBEK_CYRILLIC, "uz" , LANG_UZBEK , SUBLANG_UZBEK_CYRILLIC , wxLayout_LeftToRight, "Uzbek (Cyrillic)") LNG(wxLANGUAGE_UZBEK_LATIN, "uz" , LANG_UZBEK , SUBLANG_UZBEK_LATIN , wxLayout_LeftToRight, "Uzbek (Latin)") + LNG(wxLANGUAGE_VALENCIAN, "ca_ES@valencia", 0 , 0 , wxLayout_LeftToRight, "Valencian (Southern Catalan)") LNG(wxLANGUAGE_VIETNAMESE, "vi_VN", LANG_VIETNAMESE, SUBLANG_DEFAULT , wxLayout_LeftToRight, "Vietnamese") LNG(wxLANGUAGE_VOLAPUK, "vo" , 0 , 0 , wxLayout_LeftToRight, "Volapuk") LNG(wxLANGUAGE_WELSH, "cy" , 0 , 0 , wxLayout_LeftToRight, "Welsh") @@ -3500,6 +3948,7 @@ void wxLocale::InitLanguagesDB() LNG(wxLANGUAGE_YORUBA, "yo" , 0 , 0 , wxLayout_LeftToRight, "Yoruba") LNG(wxLANGUAGE_ZHUANG, "za" , 0 , 0 , wxLayout_LeftToRight, "Zhuang") LNG(wxLANGUAGE_ZULU, "zu" , 0 , 0 , wxLayout_LeftToRight, "Zulu") + } #undef LNG