X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/c86f1403c3737c07d58676a203f4707942684a01..6842d18650b487249b4ae29d0d7e17231b24cde8:/src/common/intl.cpp diff --git a/src/common/intl.cpp b/src/common/intl.cpp index 6cb46f1be0..1172719390 100644 --- a/src/common/intl.cpp +++ b/src/common/intl.cpp @@ -6,7 +6,7 @@ // Created: 29/01/98 // RCS-ID: $Id$ // Copyright: (c) 1998 Vadim Zeitlin -// Licence: wxWindows license +// Licence: wxWindows license ///////////////////////////////////////////////////////////////////////////// // ============================================================================ @@ -18,18 +18,21 @@ // ---------------------------------------------------------------------------- #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 +#if wxUSE_INTL + // standard headers #include +#include // wxWindows #include "wx/defs.h" @@ -37,10 +40,54 @@ #include "wx/intl.h" #include "wx/file.h" #include "wx/log.h" +#include "wx/debug.h" #include "wx/utils.h" #include +// ---------------------------------------------------------------------------- +// simple types +// ---------------------------------------------------------------------------- + +// this should *not* be wxChar, this type must have exactly 8 bits! +typedef unsigned char size_t8; + +#ifdef __WXMSW__ + #if defined(__WIN16__) + typedef unsigned long size_t32; + #elif defined(__WIN32__) + typedef unsigned int size_t32; + #else + // Win64 will have different type sizes + #error "Please define a 32 bit type" + #endif +#else // !Windows + // SIZEOF_XXX are defined by configure + #if defined(SIZEOF_INT) && (SIZEOF_INT == 4) + typedef unsigned int size_t32; + #elif defined(SIZEOF_LONG) && (SIZEOF_LONG == 4) + typedef unsigned long size_t32; + #else + // assume sizeof(int) == 4 - what else can we do + typedef unsigned int size_t32; + + // ... but at least check it during run time + static class IntSizeChecker + { + public: + IntSizeChecker() + { + // Asserting a sizeof directly causes some compilers to + // issue a "using constant in a conditional expression" warning + size_t intsize = sizeof(int); + + wxASSERT_MSG( intsize == 4, + "size_t32 is incorrectly defined!" ); + } + } intsizechecker; + #endif +#endif // Win/!Win + // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- @@ -68,8 +115,7 @@ void wxRestoreTransErrors(); // get the current state bool wxIsLoggingTransErrors(); -// get the current locale object (## may be NULL!) -extern wxLocale *wxSetLocale(wxLocale *pLocale); +static wxLocale *wxSetLocale(wxLocale *pLocale); // ---------------------------------------------------------------------------- // wxMsgCatalog corresponds to one disk-file message catalog. @@ -86,18 +132,18 @@ public: ~wxMsgCatalog(); // load the catalog from disk (szDirPrefix corresponds to language) - bool Load(const char *szDirPrefix, const char *szName); + bool Load(const wxChar *szDirPrefix, const wxChar *szName); bool IsLoaded() const { return m_pData != NULL; } // get name of the catalog - const char *GetName() const { return m_pszName; } + const wxChar *GetName() const { return m_pszName; } // get the translated string: returns NULL if not found const char *GetString(const char *sz) const; // 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 @@ -112,21 +158,21 @@ private: struct wxMsgCatalogHeader { size_t32 magic, // offset +00: magic id - revision, // +04: revision - numStrings; // +08: number of strings in the file + 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 + ofsTransTable; // +10: start of translated string table size_t32 nHashSize, // +14: hash table size - ofsHashTable; // +18: offset of hash table start - }; - + ofsHashTable; // +18: offset of hash table start + }; + // all data is stored here, NULL if no data loaded size_t8 *m_pData; - + // data description - size_t32 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 - size_t32 *m_pHashTable; // pointer to hash table + size_t32 *m_pHashTable; // pointer to hash table wxMsgTableEntry *m_pOrigTable, // pointer to original strings *m_pTransTable; // translated @@ -145,9 +191,16 @@ private: bool m_bSwapped; // wrong endianness? - char *m_pszName; // name of the domain + wxChar *m_pszName; // name of the domain }; +// ---------------------------------------------------------------------------- +// global variables +// ---------------------------------------------------------------------------- + +// the list of the directories to search for message catalog files +static wxArrayString s_searchPrefixes; + // ============================================================================ // implementation // ============================================================================ @@ -180,60 +233,104 @@ size_t32 wxMsgCatalog::GetHash(const char *sz) // swap the 2 halves of 32 bit integer if needed 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() -{ - wxDELETEA(m_pData); - wxDELETEA(m_pszName); +wxMsgCatalog::~wxMsgCatalog() +{ + wxDELETEA(m_pData); + wxDELETEA(m_pszName); } +// 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 wxChar *prefix, + const wxChar *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 + << wxT("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 wxChar *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(wxT("."), lang) << wxPATH_SEP; + + // and finally add some standard ones + searchPath + << GetAllMsgCatalogSubdirs(wxT("/usr/share/locale"), lang) << wxPATH_SEP + << GetAllMsgCatalogSubdirs(wxT("/usr/lib/locale"), lang) << wxPATH_SEP + << GetAllMsgCatalogSubdirs(wxT("/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) +bool wxMsgCatalog::Load(const wxChar *szDirPrefix, const wxChar *szName0) { - // search order (assume language 'foo') is - // 1) $LC_PATH/foo/LC_MESSAGES (if LC_PATH set) - // 2) ./foo/LC_MESSAGES - // 3) ./foo - // 4) . (Added by JACS) - // - // under UNIX we search also in: - // 5) /usr/share/locale/foo/LC_MESSAGES (Linux) - // 6) /usr/lib/locale/foo/LC_MESSAGES (Solaris) - #define MSG_PATH FILE_SEP_PATH + "LC_MESSAGES" PATH_SEP - - wxString strPath(""); - const char *pszLcPath = getenv("LC_PATH"); + /* We need to handle locales like de_AT.iso-8859-1 + For this we first chop off the .CHARSET specifier and ignore it. + FIXME: UNICODE SUPPORT: must use CHARSET specifier! + */ + wxString szName = szName0; + if(szName.Find('.') != -1) // contains a dot + szName = szName.Left(szName.Find('.')); + + // FIXME VZ: I forgot the exact meaning of LC_PATH - anyone to remind me? + // KB: search path where to find the mo files, probably : delimited +#if 0 + const wxChar *pszLcPath = wxGetenv("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 - #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 wxChar *sublocale = wxStrchr(szDirPrefix, wxT('_')); + 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; @@ -241,21 +338,21 @@ bool wxMsgCatalog::Load(const char *szDirPrefix, const char *szName) // 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, strPath.c_str()); + NoTransErr noTransErr; + wxLogVerbose(wxT("looking for catalog '%s' in path '%s'."), + szName.c_str(), searchPath.c_str()); wxString strFullName; - if ( !wxFindFileInPath(&strFullName, strPath, strFile) ) { - wxLogWarning(_("catalog file for domain '%s' not found."), szName); + if ( !wxFindFileInPath(&strFullName, searchPath, strFile) ) { + wxLogWarning(_("catalog file for domain '%s' not found."), szName.c_str()); return FALSE; } // open file wxLogVerbose(_("using catalog '%s' from '%s'."), - szName, strFullName.c_str()); - + szName.c_str(), strFullName.c_str()); + wxFile fileMsg(strFullName); if ( !fileMsg.IsOpened() ) return FALSE; @@ -271,41 +368,39 @@ bool wxMsgCatalog::Load(const char *szDirPrefix, const char *szName) wxDELETEA(m_pData); return FALSE; } - + // examine header bool bValid = (size_t)nSize > sizeof(wxMsgCatalogHeader); - - wxMsgCatalogHeader *pHeader; - if ( bValid ) { - pHeader = (wxMsgCatalogHeader *)m_pData; + 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()); - + wxDELETEA(m_pData); return FALSE; } - + // initialize m_numStrings = Swap(pHeader->numStrings); - m_pOrigTable = (wxMsgTableEntry *)(m_pData + + m_pOrigTable = (wxMsgTableEntry *)(m_pData + Swap(pHeader->ofsOrigTable)); - m_pTransTable = (wxMsgTableEntry *)(m_pData + + m_pTransTable = (wxMsgTableEntry *)(m_pData + Swap(pHeader->ofsTransTable)); m_nHashSize = Swap(pHeader->nHashSize); m_pHashTable = (size_t32 *)(m_pData + Swap(pHeader->ofsHashTable)); - m_pszName = new char[strlen(szName) + 1]; - strcpy(m_pszName, szName); + m_pszName = new wxChar[wxStrlen(szName) + 1]; + wxStrcpy(m_pszName, szName); // everything is fine return TRUE; @@ -318,17 +413,17 @@ const char *wxMsgCatalog::GetString(const char *szOrig) const return NULL; if ( HasHashTable() ) { // use hash table for lookup if possible - size_t32 nHashVal = GetHash(szOrig); + size_t32 nHashVal = GetHash(szOrig); size_t32 nIndex = nHashVal % m_nHashSize; size_t32 nIncr = 1 + (nHashVal % (m_nHashSize - 2)); - + while ( TRUE ) { size_t32 nStr = Swap(m_pHashTable[nIndex]); if ( nStr == 0 ) return NULL; - - if ( strcmp(szOrig, StringAtOfs(m_pOrigTable, nStr - 1)) == 0 ) + + if ( wxStrcmp(szOrig, StringAtOfs(m_pOrigTable, nStr - 1)) == 0 ) return StringAtOfs(m_pTransTable, nStr - 1); if ( nIndex >= m_nHashSize - nIncr) @@ -343,7 +438,7 @@ const char *wxMsgCatalog::GetString(const char *szOrig) const current; while ( bottom < top ) { current = (bottom + top) / 2; - int res = strcmp(szOrig, StringAtOfs(m_pOrigTable, current)); + int res = wxStrcmp(szOrig, StringAtOfs(m_pOrigTable, current)); if ( res < 0 ) top = current; else if ( res > 0 ) @@ -368,9 +463,9 @@ wxLocale::wxLocale() } // NB: this function has (desired) side effect of changing current locale -bool wxLocale::Init(const char *szName, - const char *szShort, - const char *szLocale, +bool wxLocale::Init(const wxChar *szName, + const wxChar *szShort, + const wxChar *szLocale, bool bLoadDefault) { m_strLocale = szName; @@ -378,67 +473,85 @@ bool wxLocale::Init(const char *szName, // change current locale (default: same as long name) if ( szLocale == NULL ) - szLocale = szName; - m_pszOldLocale = setlocale(LC_ALL, szLocale); + { + // the argument to setlocale() + szLocale = szShort; + } + m_pszOldLocale = wxSetlocale(LC_ALL, szLocale); if ( m_pszOldLocale == NULL ) wxLogError(_("locale '%s' can not be set."), szLocale); // 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 - m_strShort = wxToLower(szLocale[0]) + wxToLower(szLocale[1]); + // FIXME I don't know how these 2 letter abbreviations are formed, + // this wild guess is surely wrong + m_strShort = tolower(szLocale[0]) + tolower(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 ) - bOk = AddCatalog("wxstd"); + bOk = AddCatalog(wxT("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); + wxSetlocale(LC_ALL, m_pszOldLocale); } // get the translation of given string in current locale -const char *wxLocale::GetString(const char *szOrigString, - const char *szDomain) const +const wxMB2WXbuf wxLocale::GetString(const wxChar *szOrigString, + const wxChar *szDomain) const { - wxASSERT( szOrigString != NULL ); // would be pretty silly + if ( wxIsEmpty(szOrigString) ) + return szDomain; const char *pszTrans = NULL; +#if wxUSE_UNICODE + const wxWX2MBbuf szOrgString = wxConvCurrent->cWX2MB(szOrigString); +#else // ANSI + #define szOrgString szOrigString +#endif // Unicode/ANSI wxMsgCatalog *pMsgCat; if ( szDomain != NULL ) { pMsgCat = FindCatalog(szDomain); - + // does the catalog exist? if ( pMsgCat != NULL ) - pszTrans = pMsgCat->GetString(szOrigString); + pszTrans = pMsgCat->GetString(szOrgString); } else { // search in all domains for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext ) { - pszTrans = pMsgCat->GetString(szOrigString); + pszTrans = pMsgCat->GetString(szOrgString); if ( pszTrans != NULL ) // take the first found break; } @@ -446,60 +559,76 @@ 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'."), - szOrigString, szDomain, m_strLocale.c_str()); + szOrigString, szDomain, m_strLocale.c_str()); + } else + { wxLogWarning(_("string '%s' not found in locale '%s'."), - szOrigString, m_strLocale.c_str()); + szOrigString, m_strLocale.c_str()); + } } - return szOrigString; + return (wxMB2WXbuf)(szOrigString); } else - return pszTrans; + { + return wxConvertMB2WX(pszTrans); // or preferably wxCSConv(charset).cMB2WX(pszTrans) or something, + // a macro similar to wxConvertMB2WX could be written for that + } + + #undef szOrgString } // find catalog by name in a linked list, return NULL if !found -wxMsgCatalog *wxLocale::FindCatalog(const char *szDomain) const +wxMsgCatalog *wxLocale::FindCatalog(const wxChar *szDomain) const { // linear search in the linked list wxMsgCatalog *pMsgCat; for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext ) { - if ( Stricmp(pMsgCat->GetName(), szDomain) == 0 ) + if ( wxStricmp(pMsgCat->GetName(), szDomain) == 0 ) return pMsgCat; } - + return NULL; } // check if the given catalog is loaded -bool wxLocale::IsLoaded(const char *szDomain) const +bool wxLocale::IsLoaded(const wxChar *szDomain) const { return FindCatalog(szDomain) != NULL; } // add a catalog to our linked list -bool wxLocale::AddCatalog(const char *szDomain) +bool wxLocale::AddCatalog(const wxChar *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; } } @@ -532,16 +661,19 @@ bool wxIsLoggingTransErrors() // ------------------------------ // the current locale object -wxLocale *g_pLocale = NULL; +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; } + +#endif // wxUSE_INTL +