X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/7502ba2975bfd348dc50fef2a978e91f543d6bfe..1363811bb6338fb0dad6c16cfa47f46fb1eb3b99:/src/common/intl.cpp diff --git a/src/common/intl.cpp b/src/common/intl.cpp index eb52e900b9..aee8a90b39 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -6,11 +6,11 @@ // Created: 29/01/98 // RCS-ID: $Id$ // Copyright: (c) 1998 Vadim Zeitlin -// Licence: wxWindows license +// Licence: wxWindows license ///////////////////////////////////////////////////////////////////////////// // ============================================================================ -// declarations +// declaration // ============================================================================ // ---------------------------------------------------------------------------- @@ -18,59 +18,64 @@ // ---------------------------------------------------------------------------- #ifdef __GNUG__ -#pragma implementation "intl.h" + #pragma implementation "intl.h" #endif // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" #ifdef __BORLANDC__ -#pragma hdrstop + #pragma hdrstop #endif -// wxWindows -#ifndef WX_PRECOMP - #include "wx/defs.h" - #include "wx/string.h" -#endif //WX_PRECOMP +// standard headers +#include +// wxWindows +#include "wx/defs.h" +#include "wx/string.h" #include "wx/intl.h" #include "wx/file.h" #include "wx/log.h" #include "wx/utils.h" -// standard headers -#include #include +// ---------------------------------------------------------------------------- +// simple types +// ---------------------------------------------------------------------------- + +// FIXME adjust if necessary +typedef unsigned char size_t8; +typedef unsigned long size_t32; + // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- // magic number identifying the .mo format file -const uint32 MSGCATALOG_MAGIC = 0x950412de; -const uint32 MSGCATALOG_MAGIC_SW = 0xde120495; +const size_t32 MSGCATALOG_MAGIC = 0x950412de; +const size_t32 MSGCATALOG_MAGIC_SW = 0xde120495; // extension of ".mo" files #define MSGCATALOG_EXTENSION ".mo" // ---------------------------------------------------------------------------- -// global functions (private to this module) +// global functions // ---------------------------------------------------------------------------- // suppress further error messages about missing translations // (if you don't have one catalog file, you wouldn't like to see the // error message for each string in it, so normally it's given only // once) -static void wxSuppressTransErrors(); +void wxSuppressTransErrors(); // restore the logging -static void wxRestoreTransErrors(); +void wxRestoreTransErrors(); // get the current state -static bool wxIsLoggingTransErrors(); +bool wxIsLoggingTransErrors(); -// get the current locale object (@@ may be NULL!) static wxLocale *wxSetLocale(wxLocale *pLocale); // ---------------------------------------------------------------------------- @@ -99,47 +104,47 @@ public: // public variable pointing to the next element in a linked list (or NULL) wxMsgCatalog *m_pNext; - + private: // this implementation is binary compatible with GNU gettext() version 0.10 // an entry in the string table struct wxMsgTableEntry { - uint32 nLen; // length of the string - uint32 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 { - uint32 magic, // offset +00: magic id - revision, // +04: revision - numStrings; // +08: number of strings in the file - uint32 ofsOrigTable, // +0C: start of original string table - ofsTransTable; // +10: start of translated string table - uint32 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, NULL if no data loaded - uint8 *m_pData; - + size_t8 *m_pData; + // data description - uint32 m_numStrings, // number of strings in this domain + size_t32 m_numStrings, // number of strings in this domain m_nHashSize; // number of entries in hash table - uint32 *m_pHashTable; // pointer to hash table + size_t32 *m_pHashTable; // pointer to hash table wxMsgTableEntry *m_pOrigTable, // pointer to original strings *m_pTransTable; // translated - const char *StringAtOfs(wxMsgTableEntry *pTable, uint32 index) const + const char *StringAtOfs(wxMsgTableEntry *pTable, size_t32 index) const { return (const char *)(m_pData + Swap(pTable[index].ofsString)); } // utility functions // calculate the hash value of given string - static inline uint32 GetHash(const char *sz); + static inline size_t32 GetHash(const char *sz); // big<->little endian - inline uint32 Swap(uint32 ui) const; + inline size_t32 Swap(size_t32 ui) const; // internal state bool HasHashTable() const // true if hash table is present @@ -150,6 +155,13 @@ private: char *m_pszName; // name of the domain }; +// ---------------------------------------------------------------------------- +// global variables +// ---------------------------------------------------------------------------- + +// the list of the directories to search for message catalog files +static wxArrayString s_searchPrefixes; + // ============================================================================ // implementation // ============================================================================ @@ -160,16 +172,16 @@ private: // calculate hash value using the so called hashpjw function by P.J. Weinberger // [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools] -uint32 wxMsgCatalog::GetHash(const char *sz) +size_t32 wxMsgCatalog::GetHash(const char *sz) { - #define HASHWORDBITS 32 // the length of uint32 + #define HASHWORDBITS 32 // the length of size_t32 - uint32 hval = 0; - uint32 g; + size_t32 hval = 0; + size_t32 g; while ( *sz != '\0' ) { hval <<= 4; - hval += (uint32)*sz++; - g = hval & ((uint32)0xf << (HASHWORDBITS - 4)); + hval += (size_t32)*sz++; + g = hval & ((size_t32)0xf << (HASHWORDBITS - 4)); if ( g != 0 ) { hval ^= g >> (HASHWORDBITS - 8); hval ^= g; @@ -180,64 +192,97 @@ uint32 wxMsgCatalog::GetHash(const char *sz) } // swap the 2 halves of 32 bit integer if needed -uint32 wxMsgCatalog::Swap(uint32 ui) const +size_t32 wxMsgCatalog::Swap(size_t32 ui) const { - return m_bSwapped ? (ui << 24) | ((ui & 0xff00) << 8) | + return m_bSwapped ? (ui << 24) | ((ui & 0xff00) << 8) | ((ui >> 8) & 0xff00) | (ui >> 24) : ui; } -wxMsgCatalog::wxMsgCatalog() -{ +wxMsgCatalog::wxMsgCatalog() +{ m_pData = NULL; m_pszName = NULL; } -wxMsgCatalog::~wxMsgCatalog() -{ - DELETEA(m_pData); - DELETEA(m_pszName); +wxMsgCatalog::~wxMsgCatalog() +{ + wxDELETEA(m_pData); + wxDELETEA(m_pszName); } -// a helper class which suppresses all translation error messages -// from the moment of it's creation until it's destruction +// small class to suppress the translation erros until exit from current scope class NoTransErr { - public: +public: NoTransErr() { wxSuppressTransErrors(); } ~NoTransErr() { wxRestoreTransErrors(); } }; - + +// return all directories to search for given prefix +static wxString GetAllMsgCatalogSubdirs(const char *prefix, + const char *lang) +{ + wxString searchPath; + + // search first in prefix/fr/LC_MESSAGES, then in prefix/fr and finally in + // prefix (assuming the language is 'fr') + searchPath << prefix << wxFILE_SEP_PATH << lang << wxFILE_SEP_PATH + << "LC_MESSAGES" << wxPATH_SEP + << prefix << wxFILE_SEP_PATH << lang << wxPATH_SEP + << prefix << wxPATH_SEP; + + return searchPath; +} + +// construct the search path for the given language +static wxString GetFullSearchPath(const char *lang) +{ + wxString searchPath; + + // first take the entries explicitly added by the program + size_t count = s_searchPrefixes.Count(); + for ( size_t n = 0; n < count; n++ ) + { + searchPath << GetAllMsgCatalogSubdirs(s_searchPrefixes[n], lang) + << wxPATH_SEP; + } + + // then take the current directory + // FIXME it should be the directory of the executable + searchPath << GetAllMsgCatalogSubdirs(".", lang) << wxPATH_SEP; + + // and finally add some standard ones + searchPath + << GetAllMsgCatalogSubdirs("/usr/share/locale", lang) << wxPATH_SEP + << GetAllMsgCatalogSubdirs("/usr/lib/locale", lang) << wxPATH_SEP + << GetAllMsgCatalogSubdirs("/usr/local/share/locale", lang); + + return searchPath; +} + // open disk file and read in it's contents bool wxMsgCatalog::Load(const char *szDirPrefix, const char *szName) { - // search order (assume language 'lang') is - // 1) $LC_PATH/lang/LC_MESSAGES (if LC_PATH set) - // 2) ./lang/LC_MESSAGES - // 3) ./lang - // 4) . (Added by JACS) - // - // under UNIX we search also in: - // 5) /usr/share/locale/lang/LC_MESSAGES (Linux) - // 6) /usr/lib/locale/lang/LC_MESSAGES (Solaris) - #define MSG_PATH FILE_SEP_PATH + "LC_MESSAGES" PATH_SEP - - wxString strPath(""); + // FIXME VZ: I forgot the exact meaning of LC_PATH - anyone to remind me? +#if 0 const char *pszLcPath = getenv("LC_PATH"); if ( pszLcPath != NULL ) - strPath += pszLcPath + wxString(szDirPrefix) + MSG_PATH; // (1) - - // NB: '<<' is unneeded between too literal strings: - // they are concatenated at compile time - strPath << "./" << wxString(szDirPrefix) + MSG_PATH // (2) - << "./" << szDirPrefix << FILE_SEP_PATH << PATH_SEP // (3) - << "." << PATH_SEP // (4) - #ifdef __UNIX__ - "/usr/share/locale/" << szDirPrefix << MSG_PATH // (5) - "/usr/lib/locale/" << szDirPrefix << MSG_PATH // (6) - #endif //UNIX - ; - + strPath += pszLcPath + wxString(szDirPrefix) + MSG_PATH; +#endif // 0 + + wxString searchPath = GetFullSearchPath(szDirPrefix); + const char *sublocale = strchr(szDirPrefix, '_'); + if ( sublocale ) + { + // 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 << GetFullSearchPath(wxString(szDirPrefix). + Left((size_t)(sublocale - szDirPrefix))) + << wxPATH_SEP; + } + wxString strFile = szName; strFile += MSGCATALOG_EXTENSION; @@ -247,81 +292,70 @@ bool wxMsgCatalog::Load(const char *szDirPrefix, const char *szName) // (we're using an object because we have several return paths) NoTransErr noTransErr; - wxLogVerbose(_("looking for catalog '%s' in path '%s'..."), - szName, strPath.c_str()); + wxLogVerbose(_("looking for catalog '%s' in path '%s'."), + szName, searchPath.c_str()); wxString strFullName; - if ( !wxFindFileInPath(&strFullName, strPath, strFile) ) { + if ( !wxFindFileInPath(&strFullName, searchPath, strFile) ) { wxLogWarning(_("catalog file for domain '%s' not found."), szName); return FALSE; } // open file - wxLogVerbose(_("catalog '%s' found in '%s'."), szName, strFullName.c_str()); - - // declare these vars here because we're using goto further down - bool bValid; - off_t nSize; + wxLogVerbose(_("using catalog '%s' from '%s'."), + szName, strFullName.c_str()); wxFile fileMsg(strFullName); if ( !fileMsg.IsOpened() ) - goto error; + return FALSE; // get the file size - nSize = fileMsg.Length(); - if ( nSize == ofsInvalid ) - goto error; + off_t nSize = fileMsg.Length(); + if ( nSize == wxInvalidOffset ) + return FALSE; // read the whole file in memory - m_pData = new uint8[nSize]; + m_pData = new size_t8[nSize]; if ( fileMsg.Read(m_pData, nSize) != nSize ) { - DELETEA(m_pData); - m_pData = NULL; - goto error; + wxDELETEA(m_pData); + return FALSE; } - + // examine header - bValid = (size_t)nSize > sizeof(wxMsgCatalogHeader); - - wxMsgCatalogHeader *pHeader; - if ( bValid ) { - pHeader = (wxMsgCatalogHeader *)m_pData; + bool bValid = (size_t)nSize > sizeof(wxMsgCatalogHeader); + wxMsgCatalogHeader *pHeader = (wxMsgCatalogHeader *)m_pData; + 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; } - + if ( !bValid ) { // it's either too short or has incorrect magic number - wxLogWarning(_("'%s' is not a valid message catalog."), - strFullName.c_str()); - - DELETEA(m_pData); - m_pData = NULL; + wxLogWarning(_("'%s' is not a valid message catalog."), strFullName.c_str()); + + wxDELETEA(m_pData); return FALSE; } - + // initialize m_numStrings = Swap(pHeader->numStrings); - m_pOrigTable = (wxMsgTableEntry *)(m_pData + Swap(pHeader->ofsOrigTable)); - m_pTransTable = (wxMsgTableEntry *)(m_pData + Swap(pHeader->ofsTransTable)); + m_pOrigTable = (wxMsgTableEntry *)(m_pData + + Swap(pHeader->ofsOrigTable)); + m_pTransTable = (wxMsgTableEntry *)(m_pData + + Swap(pHeader->ofsTransTable)); m_nHashSize = Swap(pHeader->nHashSize); - m_pHashTable = (uint32 *)(m_pData + Swap(pHeader->ofsHashTable)); + m_pHashTable = (size_t32 *)(m_pData + Swap(pHeader->ofsHashTable)); m_pszName = new char[strlen(szName) + 1]; strcpy(m_pszName, szName); // everything is fine return TRUE; - -error: - wxLogError(_("error opening message catalog '%s', not loaded."), - strFullName.c_str()); - return FALSE; } // search for a string @@ -331,16 +365,16 @@ const char *wxMsgCatalog::GetString(const char *szOrig) const return NULL; if ( HasHashTable() ) { // use hash table for lookup if possible - uint32 nHashVal = GetHash(szOrig); - uint32 nIndex = nHashVal % m_nHashSize; + size_t32 nHashVal = GetHash(szOrig); + size_t32 nIndex = nHashVal % m_nHashSize; + + size_t32 nIncr = 1 + (nHashVal % (m_nHashSize - 2)); - uint32 nIncr = 1 + (nHashVal % (m_nHashSize - 2)); - while ( TRUE ) { - uint32 nStr = Swap(m_pHashTable[nIndex]); + size_t32 nStr = Swap(m_pHashTable[nIndex]); if ( nStr == 0 ) return NULL; - + if ( strcmp(szOrig, StringAtOfs(m_pOrigTable, nStr - 1)) == 0 ) return StringAtOfs(m_pTransTable, nStr - 1); @@ -351,7 +385,7 @@ const char *wxMsgCatalog::GetString(const char *szOrig) const } } else { // no hash table: use default binary search - uint32 bottom = 0, + size_t32 bottom = 0, top = m_numStrings, current; while ( bottom < top ) { @@ -374,13 +408,21 @@ const char *wxMsgCatalog::GetString(const char *szOrig) const // wxLocale // ---------------------------------------------------------------------------- -// NB: ctor has (desired) side effect of changing current locale -wxLocale::wxLocale(const char *szName, - const char *szShort, - const char *szLocale, - bool bLoadDefault) - : m_strLocale(szName), m_strShort(szShort) +wxLocale::wxLocale() { + m_pszOldLocale = NULL; + m_pMsgCat = NULL; +} + +// NB: this function has (desired) side effect of changing current locale +bool wxLocale::Init(const char *szName, + const char *szShort, + const char *szLocale, + bool bLoadDefault) +{ + m_strLocale = szName; + m_strShort = szShort; + // change current locale (default: same as long name) if ( szLocale == NULL ) szLocale = szName; @@ -391,48 +433,61 @@ wxLocale::wxLocale(const char *szName, // the short name will be used to look for catalog files as well, // so we need something here if ( m_strShort.IsEmpty() ) { - // @@@@ I don't know how these 2 letter abbreviations are formed, - // this wild guess is almost surely wrong + // FIXME I don't know how these 2 letter abbreviations are formed, + // this wild guess is surely wrong m_strShort = wxToLower(szLocale[0]) + wxToLower(szLocale[1]); } - + // save the old locale to be able to restore it later - m_pOldLocale = wxSetLocale(this); - + m_pOldLocale = wxSetLocale(this); + // load the default catalog with wxWindows standard messages m_pMsgCat = NULL; + bool bOk = TRUE; if ( bLoadDefault ) - AddCatalog("wxstd"); + bOk = AddCatalog("wxstd"); + + return bOk; +} + +void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix) +{ + if ( s_searchPrefixes.Index(prefix) == wxNOT_FOUND ) + { + s_searchPrefixes.Add(prefix); + } + //else: already have it } // clean up wxLocale::~wxLocale() { - // free memory - wxMsgCatalog *pTmpCat; - while ( m_pMsgCat != NULL ) { - pTmpCat = m_pMsgCat; - m_pMsgCat = m_pMsgCat->m_pNext; - delete pTmpCat; - } - - // restore old locale - wxSetLocale(m_pOldLocale); - setlocale(LC_ALL, m_pszOldLocale); + // free memory + wxMsgCatalog *pTmpCat; + while ( m_pMsgCat != NULL ) { + pTmpCat = m_pMsgCat; + m_pMsgCat = m_pMsgCat->m_pNext; + delete pTmpCat; + } + + // restore old locale + wxSetLocale(m_pOldLocale); + setlocale(LC_ALL, m_pszOldLocale); } // get the translation of given string in current locale -const char *wxLocale::GetString(const char *szOrigString, +const char *wxLocale::GetString(const char *szOrigString, const char *szDomain) const { - wxASSERT( szOrigString != NULL ); // would be pretty silly + if ( IsEmpty(szOrigString) ) + return szDomain; const char *pszTrans = NULL; wxMsgCatalog *pMsgCat; if ( szDomain != NULL ) { pMsgCat = FindCatalog(szDomain); - + // does the catalog exist? if ( pMsgCat != NULL ) pszTrans = pMsgCat->GetString(szOrigString); @@ -448,17 +503,27 @@ const char *wxLocale::GetString(const char *szOrigString, if ( pszTrans == NULL ) { if ( wxIsLoggingTransErrors() ) { - // suppress further error messages - // (do it before LogWarning to prevent infinite recursion!) + // suppress further error messages if we're not debugging: this avoids + // flooding the user with messages about each and every missing string if, + // for example, a whole catalog file is missing. + + // do it before calling LogWarning to prevent infinite recursion! +#ifdef __WXDEBUG__ + NoTransErr noTransErr; +#else // !debug wxSuppressTransErrors(); - +#endif // debug/!debug + if ( szDomain != NULL ) - wxLogWarning(_("string '%s' not found in domain '%s'" - " for locale '%s'."), + { + wxLogWarning(_("string '%s' not found in domain '%s' for locale '%s'."), szOrigString, szDomain, m_strLocale.c_str()); + } else + { wxLogWarning(_("string '%s' not found in locale '%s'."), szOrigString, m_strLocale.c_str()); + } } return szOrigString; @@ -476,7 +541,7 @@ wxMsgCatalog *wxLocale::FindCatalog(const char *szDomain) const if ( Stricmp(pMsgCat->GetName(), szDomain) == 0 ) return pMsgCat; } - + return NULL; } @@ -490,19 +555,19 @@ bool wxLocale::IsLoaded(const char *szDomain) const bool wxLocale::AddCatalog(const char *szDomain) { wxMsgCatalog *pMsgCat = new wxMsgCatalog; - + if ( pMsgCat->Load(m_strShort, szDomain) ) { // 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; - + return FALSE; } } @@ -535,11 +600,16 @@ bool wxIsLoggingTransErrors() // ------------------------------ // the current locale object -wxLocale *g_pLocale = NULL; +static wxLocale *g_pLocale = NULL; + +wxLocale *wxGetLocale() +{ + 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; }