Add support for storing translations in win32 resources.
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@64155 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -446,6 +446,8 @@ All:
|
|||||||
- Added IEC and SI units support to GetHumanReadableSize() (Julien Weinzorn).
|
- Added IEC and SI units support to GetHumanReadableSize() (Julien Weinzorn).
|
||||||
- Add convenient wxString::ToStd{String,Wstring}() helpers.
|
- Add convenient wxString::ToStd{String,Wstring}() helpers.
|
||||||
- Added wxTranslations class to allow localization without changing locale.
|
- Added wxTranslations class to allow localization without changing locale.
|
||||||
|
- Added wxResourceTranslationsLoader for loading translations from Windows
|
||||||
|
resources.
|
||||||
|
|
||||||
Unix:
|
Unix:
|
||||||
|
|
||||||
|
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#if wxUSE_INTL
|
#if wxUSE_INTL
|
||||||
|
|
||||||
|
#include "wx/buffer.h"
|
||||||
#include "wx/language.h"
|
#include "wx/language.h"
|
||||||
|
|
||||||
#if !wxUSE_UNICODE
|
#if !wxUSE_UNICODE
|
||||||
@@ -90,9 +91,11 @@ public:
|
|||||||
// check if the given catalog is loaded
|
// check if the given catalog is loaded
|
||||||
bool IsLoaded(const wxString& domain) const;
|
bool IsLoaded(const wxString& domain) const;
|
||||||
|
|
||||||
// load catalog data directly from file
|
// load catalog data directly from file or memory
|
||||||
bool LoadCatalogFile(const wxString& filename,
|
bool LoadCatalogFile(const wxString& filename,
|
||||||
const wxString& domain = wxEmptyString);
|
const wxString& domain = wxEmptyString);
|
||||||
|
bool LoadCatalogData(const wxScopedCharTypeBuffer<char>& data,
|
||||||
|
const wxString& domain = wxEmptyString);
|
||||||
|
|
||||||
// access to translations
|
// access to translations
|
||||||
const wxString& GetString(const wxString& origString,
|
const wxString& GetString(const wxString& origString,
|
||||||
@@ -148,6 +151,7 @@ public:
|
|||||||
const wxString& domain, const wxString& lang) = 0;
|
const wxString& domain, const wxString& lang) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// standard wxTranslationsLoader implementation, using filesystem
|
// standard wxTranslationsLoader implementation, using filesystem
|
||||||
class WXDLLIMPEXP_BASE wxFileTranslationsLoader
|
class WXDLLIMPEXP_BASE wxFileTranslationsLoader
|
||||||
: public wxTranslationsLoader
|
: public wxTranslationsLoader
|
||||||
@@ -160,6 +164,25 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __WINDOWS__
|
||||||
|
// loads translations from win32 resources
|
||||||
|
class WXDLLIMPEXP_BASE wxResourceTranslationsLoader
|
||||||
|
: public wxTranslationsLoader
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool LoadCatalog(wxTranslations *translations,
|
||||||
|
const wxString& domain, const wxString& lang);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// returns resource type to use for translations
|
||||||
|
virtual wxString GetResourceType() const { return "MOFILE"; }
|
||||||
|
|
||||||
|
// returns module to load resources from
|
||||||
|
virtual WXHINSTANCE GetModule() const { return 0; }
|
||||||
|
};
|
||||||
|
#endif // __WINDOWS__
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// global functions
|
// global functions
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@@ -58,7 +58,7 @@ public:
|
|||||||
|
|
||||||
Deletes previous loader and takes ownership of @a loader.
|
Deletes previous loader and takes ownership of @a loader.
|
||||||
|
|
||||||
@see wxTranslationsLoader, wxFileTranslationsLoader
|
@see wxTranslationsLoader, wxFileTranslationsLoader, wxResourceTranslationsLoader
|
||||||
*/
|
*/
|
||||||
void SetLoader(wxTranslationsLoader *loader);
|
void SetLoader(wxTranslationsLoader *loader);
|
||||||
|
|
||||||
@@ -251,7 +251,7 @@ public:
|
|||||||
|
|
||||||
Implementations must implement the LoadCatalog() method.
|
Implementations must implement the LoadCatalog() method.
|
||||||
|
|
||||||
@see wxFileTranslationsLoader
|
@see wxFileTranslationsLoader, wxResourceTranslationsLoader
|
||||||
|
|
||||||
@since 2.9.1
|
@since 2.9.1
|
||||||
*/
|
*/
|
||||||
@@ -308,6 +308,53 @@ public:
|
|||||||
static void AddCatalogLookupPathPrefix(const wxString& prefix);
|
static void AddCatalogLookupPathPrefix(const wxString& prefix);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
This loader makes it possible to load translations from Windows
|
||||||
|
resources.
|
||||||
|
|
||||||
|
If you wish to store translation MO files in resources, you have to
|
||||||
|
enable this loader before calling wxTranslations::AddCatalog() or
|
||||||
|
wxLocale::AddCatalog():
|
||||||
|
|
||||||
|
@code
|
||||||
|
wxTranslations::Get()->SetLoader(new wxResourceTranslationsLoader);
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
Translations are stored in resources as compiled MO files, with type
|
||||||
|
set to "MOFILE" (unless you override GetResourceType()) and name
|
||||||
|
consisting of the domain, followed by underscore, followed by language
|
||||||
|
identification. For example, the relevant part of .rc file would look
|
||||||
|
like this:
|
||||||
|
|
||||||
|
@code
|
||||||
|
myapp_de MOFILE "catalogs/de/myapp.mo"
|
||||||
|
myapp_fr MOFILE "catalogs/fr/myapp.mo"
|
||||||
|
myapp_en_GB MOFILE "catalogs/en_GB/myapp.mo"
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
This class is only available on Windows.
|
||||||
|
|
||||||
|
@since 2.9.1
|
||||||
|
|
||||||
|
*/
|
||||||
|
class wxResourceTranslationsLoader : public wxTranslationsLoader
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
Returns resource type to use for translations.
|
||||||
|
|
||||||
|
Default type is "MOFILE".
|
||||||
|
*/
|
||||||
|
virtual wxString GetResourceType() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns handle of the module to load resources from.
|
||||||
|
|
||||||
|
By default, the main executable is used.
|
||||||
|
*/
|
||||||
|
virtual WXHINSTANCE GetModule() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
@@ -809,13 +809,17 @@ WX_DECLARE_EXPORTED_STRING_HASH_MAP(wxString, wxMessagesHash);
|
|||||||
class wxMsgCatalogFile
|
class wxMsgCatalogFile
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
typedef wxScopedCharTypeBuffer<char> DataBuffer;
|
||||||
|
|
||||||
// ctor & dtor
|
// ctor & dtor
|
||||||
wxMsgCatalogFile();
|
wxMsgCatalogFile();
|
||||||
~wxMsgCatalogFile();
|
~wxMsgCatalogFile();
|
||||||
|
|
||||||
// load the catalog from disk
|
// load the catalog from disk
|
||||||
bool Load(const wxString& filename,
|
bool LoadFile(const wxString& filename,
|
||||||
wxPluralFormsCalculatorPtr& rPluralFormsCalculator);
|
wxPluralFormsCalculatorPtr& rPluralFormsCalculator);
|
||||||
|
bool LoadData(const DataBuffer& data,
|
||||||
|
wxPluralFormsCalculatorPtr& rPluralFormsCalculator);
|
||||||
|
|
||||||
// fills the hash with string-translation pairs
|
// fills the hash with string-translation pairs
|
||||||
bool FillHash(wxMessagesHash& hash, const wxString& msgIdCharset) const;
|
bool FillHash(wxMessagesHash& hash, const wxString& msgIdCharset) const;
|
||||||
@@ -847,7 +851,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// all data is stored here
|
// all data is stored here
|
||||||
wxMemoryBuffer m_data;
|
DataBuffer m_data;
|
||||||
|
|
||||||
// data description
|
// data description
|
||||||
size_t32 m_numStrings; // number of strings in this domain
|
size_t32 m_numStrings; // number of strings in this domain
|
||||||
@@ -865,25 +869,18 @@ private:
|
|||||||
: ui;
|
: ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
// just return the pointer to the start of the data as "char *" to
|
|
||||||
// facilitate doing pointer arithmetic with it
|
|
||||||
char *StringData() const
|
|
||||||
{
|
|
||||||
return static_cast<char *>(m_data.GetData());
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *StringAtOfs(wxMsgTableEntry *pTable, size_t32 n) const
|
const char *StringAtOfs(wxMsgTableEntry *pTable, size_t32 n) const
|
||||||
{
|
{
|
||||||
const wxMsgTableEntry * const ent = pTable + n;
|
const wxMsgTableEntry * const ent = pTable + n;
|
||||||
|
|
||||||
// this check could fail for a corrupt message catalog
|
// this check could fail for a corrupt message catalog
|
||||||
size_t32 ofsString = Swap(ent->ofsString);
|
size_t32 ofsString = Swap(ent->ofsString);
|
||||||
if ( ofsString + Swap(ent->nLen) > m_data.GetDataLen())
|
if ( ofsString + Swap(ent->nLen) > m_data.length())
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return StringData() + ofsString;
|
return m_data.data() + ofsString;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool m_bSwapped; // wrong endianness?
|
bool m_bSwapped; // wrong endianness?
|
||||||
@@ -908,9 +905,13 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// load the catalog from disk
|
// load the catalog from disk
|
||||||
bool Load(const wxString& filename,
|
bool LoadFile(const wxString& filename,
|
||||||
const wxString& domain,
|
const wxString& domain,
|
||||||
const wxString& msgIdCharset);
|
const wxString& msgIdCharset);
|
||||||
|
|
||||||
|
bool LoadData(const wxScopedCharTypeBuffer<char>& data,
|
||||||
|
const wxString& domain,
|
||||||
|
const wxString& msgIdCharset);
|
||||||
|
|
||||||
// get name of the catalog
|
// get name of the catalog
|
||||||
wxString GetDomain() const { return m_domain; }
|
wxString GetDomain() const { return m_domain; }
|
||||||
@@ -947,8 +948,8 @@ wxMsgCatalogFile::~wxMsgCatalogFile()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// open disk file and read in it's contents
|
// open disk file and read in it's contents
|
||||||
bool wxMsgCatalogFile::Load(const wxString& filename,
|
bool wxMsgCatalogFile::LoadFile(const wxString& filename,
|
||||||
wxPluralFormsCalculatorPtr& rPluralFormsCalculator)
|
wxPluralFormsCalculatorPtr& rPluralFormsCalculator)
|
||||||
{
|
{
|
||||||
wxFile fileMsg(filename);
|
wxFile fileMsg(filename);
|
||||||
if ( !fileMsg.IsOpened() )
|
if ( !fileMsg.IsOpened() )
|
||||||
@@ -962,17 +963,36 @@ bool wxMsgCatalogFile::Load(const wxString& filename,
|
|||||||
size_t nSize = wx_truncate_cast(size_t, lenFile);
|
size_t nSize = wx_truncate_cast(size_t, lenFile);
|
||||||
wxASSERT_MSG( nSize == lenFile + size_t(0), wxS("message catalog bigger than 4GB?") );
|
wxASSERT_MSG( nSize == lenFile + size_t(0), wxS("message catalog bigger than 4GB?") );
|
||||||
|
|
||||||
|
wxMemoryBuffer filedata;
|
||||||
|
|
||||||
// read the whole file in memory
|
// read the whole file in memory
|
||||||
if ( fileMsg.Read(m_data.GetWriteBuf(nSize), nSize) != lenFile )
|
if ( fileMsg.Read(filedata.GetWriteBuf(nSize), nSize) != lenFile )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_data.UngetWriteBuf(nSize);
|
filedata.UngetWriteBuf(nSize);
|
||||||
|
|
||||||
|
bool ok = LoadData
|
||||||
|
(
|
||||||
|
DataBuffer::CreateOwned((char*)filedata.release(), nSize),
|
||||||
|
rPluralFormsCalculator
|
||||||
|
);
|
||||||
|
if ( !ok )
|
||||||
|
{
|
||||||
|
wxLogWarning(_("'%s' is not a valid message catalog."), filename.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool wxMsgCatalogFile::LoadData(const DataBuffer& data,
|
||||||
|
wxPluralFormsCalculatorPtr& rPluralFormsCalculator)
|
||||||
|
{
|
||||||
// examine header
|
// examine header
|
||||||
bool bValid = m_data.GetDataLen() > sizeof(wxMsgCatalogHeader);
|
bool bValid = data.length() > sizeof(wxMsgCatalogHeader);
|
||||||
|
|
||||||
const wxMsgCatalogHeader *pHeader = (wxMsgCatalogHeader *)m_data.GetData();
|
const wxMsgCatalogHeader *pHeader = (wxMsgCatalogHeader *)data.data();
|
||||||
if ( bValid ) {
|
if ( bValid ) {
|
||||||
// we'll have to swap all the integers if it's true
|
// we'll have to swap all the integers if it's true
|
||||||
m_bSwapped = pHeader->magic == MSGCATALOG_MAGIC_SW;
|
m_bSwapped = pHeader->magic == MSGCATALOG_MAGIC_SW;
|
||||||
@@ -983,16 +1003,17 @@ bool wxMsgCatalogFile::Load(const wxString& filename,
|
|||||||
|
|
||||||
if ( !bValid ) {
|
if ( !bValid ) {
|
||||||
// it's either too short or has incorrect magic number
|
// it's either too short or has incorrect magic number
|
||||||
wxLogWarning(_("'%s' is not a valid message catalog."), filename.c_str());
|
wxLogWarning(_("Invalid message catalog."));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_data = data;
|
||||||
|
|
||||||
// initialize
|
// initialize
|
||||||
m_numStrings = Swap(pHeader->numStrings);
|
m_numStrings = Swap(pHeader->numStrings);
|
||||||
m_pOrigTable = (wxMsgTableEntry *)(StringData() +
|
m_pOrigTable = (wxMsgTableEntry *)(data.data() +
|
||||||
Swap(pHeader->ofsOrigTable));
|
Swap(pHeader->ofsOrigTable));
|
||||||
m_pTransTable = (wxMsgTableEntry *)(StringData() +
|
m_pTransTable = (wxMsgTableEntry *)(data.data() +
|
||||||
Swap(pHeader->ofsTransTable));
|
Swap(pHeader->ofsTransTable));
|
||||||
|
|
||||||
// now parse catalog's header and try to extract catalog charset and
|
// now parse catalog's header and try to extract catalog charset and
|
||||||
@@ -1177,15 +1198,32 @@ wxMsgCatalog::~wxMsgCatalog()
|
|||||||
}
|
}
|
||||||
#endif // !wxUSE_UNICODE
|
#endif // !wxUSE_UNICODE
|
||||||
|
|
||||||
bool wxMsgCatalog::Load(const wxString& filename,
|
bool wxMsgCatalog::LoadFile(const wxString& filename,
|
||||||
const wxString& domain,
|
const wxString& domain,
|
||||||
const wxString& msgIdCharset)
|
const wxString& msgIdCharset)
|
||||||
{
|
{
|
||||||
wxMsgCatalogFile file;
|
wxMsgCatalogFile file;
|
||||||
|
|
||||||
m_domain = domain;
|
m_domain = domain;
|
||||||
|
|
||||||
if ( !file.Load(filename, m_pluralFormsCalculator) )
|
if ( !file.LoadFile(filename, m_pluralFormsCalculator) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( !file.FillHash(m_messages, msgIdCharset) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wxMsgCatalog::LoadData(const wxScopedCharTypeBuffer<char>& data,
|
||||||
|
const wxString& domain,
|
||||||
|
const wxString& msgIdCharset)
|
||||||
|
{
|
||||||
|
wxMsgCatalogFile file;
|
||||||
|
|
||||||
|
m_domain = domain;
|
||||||
|
|
||||||
|
if ( !file.LoadData(data, m_pluralFormsCalculator) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if ( !file.FillHash(m_messages, msgIdCharset) )
|
if ( !file.FillHash(m_messages, msgIdCharset) )
|
||||||
@@ -1417,10 +1455,38 @@ bool wxTranslations::LoadCatalogFile(const wxString& filename,
|
|||||||
wxMsgCatalog *pMsgCat = new wxMsgCatalog;
|
wxMsgCatalog *pMsgCat = new wxMsgCatalog;
|
||||||
|
|
||||||
#if wxUSE_UNICODE
|
#if wxUSE_UNICODE
|
||||||
const bool ok = pMsgCat->Load(filename, domain, wxEmptyString/*unused*/);
|
const bool ok = pMsgCat->LoadFile(filename, domain, wxEmptyString/*unused*/);
|
||||||
#else
|
#else
|
||||||
const bool ok = pMsgCat->Load(filename, domain,
|
const bool ok = pMsgCat->LoadFile(filename, domain,
|
||||||
m_msgIdCharset[domain]);
|
m_msgIdCharset[domain]);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ( !ok )
|
||||||
|
{
|
||||||
|
// don't add it because it couldn't be loaded anyway
|
||||||
|
delete pMsgCat;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool wxTranslations::LoadCatalogData(const wxScopedCharTypeBuffer<char>& data,
|
||||||
|
const wxString& domain)
|
||||||
|
{
|
||||||
|
wxMsgCatalog *pMsgCat = new wxMsgCatalog;
|
||||||
|
|
||||||
|
#if wxUSE_UNICODE
|
||||||
|
const bool ok = pMsgCat->LoadData(data, domain, wxEmptyString/*unused*/);
|
||||||
|
#else
|
||||||
|
const bool ok = pMsgCat->LoadData(data, domain,
|
||||||
|
m_msgIdCharset[domain]);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ( !ok )
|
if ( !ok )
|
||||||
@@ -1728,6 +1794,41 @@ bool wxFileTranslationsLoader::LoadCatalog(wxTranslations *translations,
|
|||||||
return translations->LoadCatalogFile(strFullName, domain);
|
return translations->LoadCatalogFile(strFullName, domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// wxResourceTranslationsLoader
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef __WINDOWS__
|
||||||
|
bool wxResourceTranslationsLoader::LoadCatalog(wxTranslations *translations,
|
||||||
|
const wxString& domain,
|
||||||
|
const wxString& lang)
|
||||||
|
{
|
||||||
|
|
||||||
|
const void *mo_data = NULL;
|
||||||
|
size_t mo_size = 0;
|
||||||
|
|
||||||
|
const wxString resname = wxString::Format("%s_%s", domain, lang);
|
||||||
|
|
||||||
|
if ( !wxLoadUserResource(&mo_data, &mo_size,
|
||||||
|
resname,
|
||||||
|
GetResourceType(),
|
||||||
|
GetModule()) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
wxLogTrace(TRACE_I18N,
|
||||||
|
"Using catalog from Windows resource \"%s\".", resname);
|
||||||
|
|
||||||
|
const bool ok = translations->LoadCatalogData(
|
||||||
|
wxCharBuffer::CreateNonOwned(static_cast<const char*>(mo_data), mo_size));
|
||||||
|
if ( !ok )
|
||||||
|
wxLogWarning(_("Resource '%s' is not a valid message catalog."), resname);
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
#endif // __WINDOWS__
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// wxTranslationsModule module (for destruction of gs_translations)
|
// wxTranslationsModule module (for destruction of gs_translations)
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
Reference in New Issue
Block a user