// headers
// ----------------------------------------------------------------------------
-#ifdef __EMX__
-// The following define is needed by Innotek's libc to
-// make the definition of struct localeconv available.
-#define __INTERNAL_DEFS
-#endif
-
// For compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#pragma hdrstop
#endif
+#ifdef __EMX__
+// The following define is needed by Innotek's libc to
+// make the definition of struct localeconv available.
+#define __INTERNAL_DEFS
+#endif
+
#if wxUSE_INTL
#ifndef WX_PRECOMP
#include "wx/ptr_scpd.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
+ #include "wx/mac/private.h" // includes mac headers
+#endif
+
+#if defined(__WXOSX__)
+ #include "wx/mac/corefoundation/cfstring.h"
+ #include "wx/mac/corefoundation/cfref.h"
#endif
// ----------------------------------------------------------------------------
~wxMsgCatalogFile();
// load the catalog from disk (szDirPrefix corresponds to language)
- bool Load(const wxChar *szDirPrefix, const wxChar *szName,
+ bool Load(const wxString& szDirPrefix, const wxString& szName,
wxPluralFormsCalculatorPtr& rPluralFormsCalculator);
// fills the hash with string-translation pairs
ofsHashTable; // +18: offset of hash table start
};
- // all data is stored here, NULL if no data loaded
- size_t8 *m_pData;
-
- // amount of memory pointed to by m_pData.
- size_t32 m_nSize;
+ // all data is stored here
+ wxMemoryBuffer m_data;
// data description
size_t32 m_numStrings; // number of strings in this domain
: ui;
}
+ // just return the pointer to the start of the data as "char *" to
+ // facilitate doing pointer arithmetic with it
+ char *StringData() const
+ {
+ return wx_static_cast(char *, m_data.GetData());
+ }
+
const char *StringAtOfs(wxMsgTableEntry *pTable, size_t32 n) const
{
const wxMsgTableEntry * const ent = pTable + n;
// this check could fail for a corrupt message catalog
size_t32 ofsString = Swap(ent->ofsString);
- if ( ofsString + Swap(ent->nLen) > m_nSize)
+ if ( ofsString + Swap(ent->nLen) > m_data.GetDataLen())
{
return NULL;
}
- return (const char *)(m_pData + ofsString);
+ return StringData() + ofsString;
}
bool m_bSwapped; // wrong endianness?
wxMsgCatalogFile::wxMsgCatalogFile()
{
- m_pData = NULL;
- m_nSize = 0;
}
wxMsgCatalogFile::~wxMsgCatalogFile()
{
- delete [] m_pData;
}
// return the directories to search for message catalogs under the given
// prefix, separated by wxPATH_SEP
static
-wxString GetMsgCatalogSubdirs(const wxChar *prefix, const wxChar *lang)
+wxString GetMsgCatalogSubdirs(const wxString& prefix, const wxString& lang)
{
- wxString searchPath;
- searchPath << prefix << wxFILE_SEP_PATH << lang;
-
- // Under Unix, the message catalogs are supposed to go into LC_MESSAGES
- // subdirectory so look there too. Note that we do it on all platforms
- // and not just Unix, because it doesn't cost much to look into one more
- // directory and doing it this way has two important benefits:
+ // Search first in Unix-standard prefix/lang/LC_MESSAGES, then in
+ // prefix/lang and finally in just prefix.
+ //
+ // Note that we use LC_MESSAGES on all platforms and not just Unix, because
+ // it doesn't cost much to look into one more directory and doing it this
+ // way has two important benefits:
// a) we don't break compatibility with wx-2.6 and older by stopping to
// look in a directory where the catalogs used to be and thus silently
// breaking apps after they are recompiled against the latest wx
// b) it makes it possible to package app's support files in the same
// way on all target platforms
- const wxString searchPathOrig(searchPath);
- searchPath << wxFILE_SEP_PATH << wxT("LC_MESSAGES")
- << wxPATH_SEP << searchPathOrig;
+ wxString pathPrefix;
+ pathPrefix << prefix << wxFILE_SEP_PATH << lang;
+
+ wxString searchPath;
+ searchPath.reserve(4*pathPrefix.length());
+ searchPath << pathPrefix << wxFILE_SEP_PATH << "LC_MESSAGES" << wxPATH_SEP
+ << prefix << wxFILE_SEP_PATH << wxPATH_SEP
+ << pathPrefix;
return searchPath;
}
// construct the search path for the given language
-static wxString GetFullSearchPath(const wxChar *lang)
+static wxString GetFullSearchPath(const wxString& lang)
{
// first take the entries explicitly added by the program
wxArrayString paths;
}
// open disk file and read in it's contents
-bool wxMsgCatalogFile::Load(const wxChar *szDirPrefix, const wxChar *szName,
+bool wxMsgCatalogFile::Load(const wxString& szDirPrefix, const wxString& szName,
wxPluralFormsCalculatorPtr& rPluralFormsCalculator)
{
wxString searchPath;
searchPath += GetFullSearchPath(szDirPrefix);
- const wxChar *sublocale = wxStrchr(szDirPrefix, wxT('_'));
- if ( sublocale )
+ size_t sublocaleIndex = szDirPrefix.find(wxT('_'));
+ 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(wxString(szDirPrefix).
- Left((size_t)(sublocale - szDirPrefix)));
+ << GetFullSearchPath(szDirPrefix.Left(sublocaleIndex));
}
// don't give translation errors here because the wxstd catalog might
wxFileName fn(szName);
fn.SetExt(_T("mo"));
+
wxString strFullName;
- if ( !wxFindFileInPath(&strFullName, searchPath, fn.GetFullPath()) ) {
+#if wxUSE_FILESYSTEM
+ wxFileSystem fileSys;
+ if ( !fileSys.FindFileInPath(&strFullName, searchPath, fn.GetFullPath()) )
+#else // !wxUSE_FILESYSTEM
+ if ( !wxFindFileInPath(&strFullName, searchPath, fn.GetFullPath()) )
+#endif // wxUSE_FILESYSTEM/!wxUSE_FILESYSTEM
+ {
wxLogVerbose(_("catalog file for domain '%s' not found."), szName);
wxLogTrace(TRACE_I18N, _T("Catalog \"%s.mo\" not found"), szName);
return false;
}
- // open file
+ // open file and read its data
wxLogVerbose(_("using catalog '%s' from '%s'."), szName, strFullName.c_str());
wxLogTrace(TRACE_I18N, _T("Using catalog \"%s\"."), strFullName.c_str());
+#if wxUSE_FILESYSTEM
+ wxFSFile * const fileMsg = fileSys.OpenFile(strFullName);
+ if ( !fileMsg )
+ return false;
+
+ 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());
+ }
+
+ delete fileMsg;
+#else // !wxUSE_FILESYSTEM
wxFile fileMsg(strFullName);
if ( !fileMsg.IsOpened() )
return false;
wxASSERT_MSG( nSize == lenFile + size_t(0), _T("message catalog bigger than 4GB?") );
// read the whole file in memory
- m_pData = new size_t8[nSize];
- if ( fileMsg.Read(m_pData, nSize) != lenFile ) {
- wxDELETEA(m_pData);
+ if ( fileMsg.Read(m_data.GetWriteBuf(nSize), nSize) != lenFile )
return false;
- }
+#endif // wxUSE_FILESYSTEM/!wxUSE_FILESYSTEM
+
// examine header
- bool bValid = nSize + (size_t)0 > sizeof(wxMsgCatalogHeader);
+ bool bValid = m_data.GetDataLen() > sizeof(wxMsgCatalogHeader);
- wxMsgCatalogHeader *pHeader = (wxMsgCatalogHeader *)m_pData;
+ 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;
// 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 *)(StringData() +
Swap(pHeader->ofsOrigTable));
- m_pTransTable = (wxMsgTableEntry *)(m_pData +
+ m_pTransTable = (wxMsgTableEntry *)(StringData() +
Swap(pHeader->ofsTransTable));
- m_nSize = (size_t32)nSize;
// now parse catalog's header and try to extract catalog charset and
// plural forms formula from it:
// the argument to setlocale()
szLocale = shortName;
- wxCHECK_MSG( szLocale, false, _T("no locale to set in wxLocale::Init()") );
+ wxCHECK_MSG( !szLocale.empty(), false,
+ _T("no locale to set in wxLocale::Init()") );
}
#ifdef __WXWINCE__
256);
if (ret != 0)
{
- m_pszOldLocale = wxStrdup(localeName);
+ m_pszOldLocale = wxStrdup(wxConvLibc.cWC2MB(localeName));
}
else
m_pszOldLocale = NULL;
// TODO: how to find languageId
// SetLocaleInfo(languageId, SORT_DEFAULT, localeName);
#else
- wxMB2WXbuf oldLocale = wxSetlocale(LC_ALL, szLocale);
+ const char *oldLocale = wxSetlocale(LC_ALL, szLocale);
if ( oldLocale )
m_pszOldLocale = wxStrdup(oldLocale);
else
#if defined(__UNIX__) && wxUSE_UNICODE && !defined(__WXMAC__)
-static wxWCharBuffer wxSetlocaleTryUTF(int c, const wxChar *lc)
+static const char *wxSetlocaleTryUTF8(int c, const wxString& lc)
{
- wxMB2WXbuf l = wxSetlocale(c, lc);
- if ( !l && lc && lc[0] != 0 )
+ const char *l = NULL;
+
+ // NB: We prefer to set UTF-8 locale if it's possible and only fall back to
+ // non-UTF-8 locale if it fails
+
+ if ( !lc.empty() )
{
wxString buf(lc);
wxString buf2;
buf2 = buf + wxT(".UTF-8");
- l = wxSetlocale(c, buf2.c_str());
+ l = wxSetlocale(c, buf2);
if ( !l )
{
buf2 = buf + wxT(".utf-8");
- l = wxSetlocale(c, buf2.c_str());
+ l = wxSetlocale(c, buf2);
}
if ( !l )
{
buf2 = buf + wxT(".UTF8");
- l = wxSetlocale(c, buf2.c_str());
+ l = wxSetlocale(c, buf2);
}
if ( !l )
{
buf2 = buf + wxT(".utf8");
- l = wxSetlocale(c, buf2.c_str());
+ l = wxSetlocale(c, buf2);
}
}
+
+ // if we can't set UTF-8 locale, try non-UTF-8 one:
+ if ( !l )
+ l = wxSetlocale(c, lc);
+
return l;
}
#else
-#define wxSetlocaleTryUTF(c, lc) wxSetlocale(c, lc)
+#define wxSetlocaleTryUTF8(c, lc) wxSetlocale(c, lc)
#endif
bool wxLocale::Init(int language, int flags)
{
+ bool ret = true;
+
int lang = language;
if (lang == wxLANGUAGE_DEFAULT)
{
// Set the locale:
#if defined(__OS2__)
- wxMB2WXbuf retloc = wxSetlocale(LC_ALL , wxEmptyString);
+ const char *retloc = wxSetlocale(LC_ALL , wxEmptyString);
#elif defined(__UNIX__) && !defined(__WXMAC__)
if (language != wxLANGUAGE_DEFAULT)
locale = info->CanonicalName;
- wxMB2WXbuf retloc = wxSetlocaleTryUTF(LC_ALL, locale);
+ const char *retloc = wxSetlocaleTryUTF8(LC_ALL, locale);
const wxString langOnly = locale.Left(2);
if ( !retloc )
{
// Some C libraries don't like xx_YY form and require xx only
- retloc = wxSetlocaleTryUTF(LC_ALL, langOnly);
+ retloc = wxSetlocaleTryUTF8(LC_ALL, langOnly);
}
#if wxUSE_FONTMAP
if ( !localeAlt.empty() )
{
- retloc = wxSetlocaleTryUTF(LC_ALL, localeAlt);
+ retloc = wxSetlocaleTryUTF8(LC_ALL, localeAlt);
if ( !retloc )
- retloc = wxSetlocaleTryUTF(LC_ALL, localeAlt.Left(2));
+ retloc = wxSetlocaleTryUTF8(LC_ALL, localeAlt.Left(2));
}
}
if ( !retloc )
- {
- wxLogError(wxT("Cannot set locale to '%s'."), locale.c_str());
- return false;
- }
+ ret = false;
#ifdef __AIX__
// at least in AIX 5.2 libc is buggy and the string returned from
//
// this contradicts IBM own docs but this is not of much help, so just work
// around it in the crudest possible manner
- wxChar *p = wxStrchr((wxChar *)retloc, _T(' '));
+ char* p = const_cast<char*>(wxStrchr(retloc, ' '));
if ( p )
- *p = _T('\0');
+ *p = '\0';
#endif // __AIX__
#elif defined(__WIN32__)
#define SETLOCALE_FAILS_ON_UNICODE_LANGS
#endif
-#if !wxUSE_UNICODE
- const
-#endif
- wxMB2WXbuf retloc = wxT("C");
+ const char *retloc = "C";
if (language != wxLANGUAGE_DEFAULT)
{
if (info->WinLang == 0)
if (locale.empty())
{
wxLogLastError(wxT("SetThreadLocale"));
- wxLogError(wxT("Cannot set locale to language %s."), name.c_str());
- return false;
+ ret = false;
}
else
{
retloc = wxSetlocale(LC_ALL, locale);
#endif
#ifdef SETLOCALE_FAILS_ON_UNICODE_LANGS
- if (codepage == 0 && (const wxChar*)retloc == NULL)
+ if (codepage == 0 && retloc == NULL)
{
- retloc = wxT("C");
+ retloc = "C";
}
#endif
}
retloc = NULL;
#endif
#ifdef SETLOCALE_FAILS_ON_UNICODE_LANGS
- if ((const wxChar*)retloc == NULL)
+ if (retloc == NULL)
{
wxChar buffer[16];
if (GetLocaleInfo(LOCALE_USER_DEFAULT,
LOCALE_IDEFAULTANSICODEPAGE, buffer, 16) > 0 &&
wxStrcmp(buffer, wxT("0")) == 0)
{
- retloc = wxT("C");
+ retloc = "C";
}
}
#endif
}
if ( !retloc )
- {
- wxLogError(wxT("Cannot set locale to language %s."), name.c_str());
- return false;
- }
+ ret = false;
#elif defined(__WXMAC__)
if (lang == wxLANGUAGE_DEFAULT)
locale = wxEmptyString;
else
locale = info->CanonicalName;
- wxMB2WXbuf retloc = wxSetlocale(LC_ALL, locale);
+ const char *retloc = wxSetlocale(LC_ALL, locale);
if ( !retloc )
{
// Some C libraries don't like xx_YY form and require xx only
retloc = wxSetlocale(LC_ALL, locale.Mid(0,2));
}
- if ( !retloc )
- {
- wxLogError(wxT("Cannot set locale to '%s'."), locale.c_str());
- return false;
- }
#else
wxUnusedVar(flags);
return false;
#endif
#ifndef WX_NO_LOCALE_SUPPORT
- bool ret = Init(name, canonical, retloc,
- (flags & wxLOCALE_LOAD_DEFAULT) != 0,
- (flags & wxLOCALE_CONV_ENCODING) != 0);
+ if ( !ret )
+ {
+ wxLogWarning(_("Cannot set locale to language \"%s\"."), name.c_str());
+
+ // continue nevertheless and try to load at least the translations for
+ // this language
+ }
+
+ if ( !Init(name, canonical, retloc,
+ (flags & wxLOCALE_LOAD_DEFAULT) != 0,
+ (flags & wxLOCALE_CONV_ENCODING) != 0) )
+ {
+ ret = false;
+ }
if (IsOk()) // setlocale() succeeded
m_language = lang;
// looking
//
// OTOH, maybe we had already found a language match and in this
- // case don't overwrite it becauce the entry for the default
+ // case don't overwrite it because the entry for the default
// country always appears first in ms_languagesDB
if ( !infoRet )
infoRet = info;
const wxString& domain) const
{
if ( origString.empty() )
- return origString;
+ return GetUntranslatedString(origString);
const wxString *trans = NULL;
wxMsgCatalog *pMsgCat;
#endif // __WXDEBUG__
if (n == size_t(-1))
- return origString;
+ return GetUntranslatedString(origString);
else
- return n == 1 ? origString : origString2;
+ return GetUntranslatedString(n == 1 ? origString : origString2);
}
return *trans;
}
+WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual,
+ wxLocaleUntranslatedStrings);
+
+/* static */
+const wxString& wxLocale::GetUntranslatedString(const wxString& str)
+{
+ static wxLocaleUntranslatedStrings s_strings;
+
+ wxLocaleUntranslatedStrings::iterator i = s_strings.find(str);
+ if ( i == s_strings.end() )
+ return *s_strings.insert(str).first;
+
+ return *i;
+}
+
wxString wxLocale::GetHeaderValue(const wxString& header,
const wxString& domain) const
{
return false;
#elif defined(__UNIX__)
-
- // Test if setting the locale works, then set it back.
- wxMB2WXbuf oldLocale = wxSetlocale(LC_ALL, wxEmptyString);
- wxMB2WXbuf tmp = wxSetlocaleTryUTF(LC_ALL, info->CanonicalName);
+
+ // 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 )
{
// Some C libraries don't like xx_YY form and require xx only
- tmp = wxSetlocaleTryUTF(LC_ALL, info->CanonicalName.Left(2));
+ tmp = wxSetlocaleTryUTF8(LC_ALL, info->CanonicalName.Left(2));
if ( !tmp )
return false;
}
// restore the original locale
- wxSetlocale(LC_ALL, oldLocale);
-#endif
+ wxSetlocale(LC_ALL, oldLocale);
+#endif
return true;
}
// accessors for locale-dependent data
// ----------------------------------------------------------------------------
-#ifdef __WXMSW__
+#if defined(__WXMSW__)
/* static */
wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat))
return str;
}
-#else // !__WXMSW__
+#elif defined(__WXOSX__)
+
+/* static */
+wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat))
+{
+ wxCFRef<CFLocaleRef> userLocaleRef(CFLocaleCopyCurrent());
+ CFTypeRef cfstr;
+ switch ( index )
+ {
+ case wxLOCALE_THOUSANDS_SEP:
+ cfstr = CFLocaleGetValue(userLocaleRef, kCFLocaleGroupingSeparator);
+ break;
+
+ case wxLOCALE_DECIMAL_POINT:
+ cfstr = CFLocaleGetValue(userLocaleRef, kCFLocaleDecimalSeparator);
+ break;
+
+ default:
+ wxFAIL_MSG( "Unknown locale info" );
+ }
+
+ wxMacCFStringHolder
+ str(CFStringCreateCopy(NULL, static_cast<CFStringRef>(cfstr)));
+ return str.AsString();
+}
+
+#else // !__WXMSW__ && !__WXMAC__
/* static */
wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory cat)
}
}
-#endif // __WXMSW__/!__WXMSW__
+#endif // platform
// ----------------------------------------------------------------------------
// global functions and variables