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:
Václav Slavík 2010-04-26 20:51:22 +00:00
parent 076c0a8ee8
commit bc71c3cdd5
4 changed files with 208 additions and 35 deletions

View File

@ -446,6 +446,8 @@ All:
- Added IEC and SI units support to GetHumanReadableSize() (Julien Weinzorn).
- Add convenient wxString::ToStd{String,Wstring}() helpers.
- Added wxTranslations class to allow localization without changing locale.
- Added wxResourceTranslationsLoader for loading translations from Windows
resources.
Unix:

View File

@ -18,6 +18,7 @@
#if wxUSE_INTL
#include "wx/buffer.h"
#include "wx/language.h"
#if !wxUSE_UNICODE
@ -90,9 +91,11 @@ public:
// check if the given catalog is loaded
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,
const wxString& domain = wxEmptyString);
bool LoadCatalogData(const wxScopedCharTypeBuffer<char>& data,
const wxString& domain = wxEmptyString);
// access to translations
const wxString& GetString(const wxString& origString,
@ -148,6 +151,7 @@ public:
const wxString& domain, const wxString& lang) = 0;
};
// standard wxTranslationsLoader implementation, using filesystem
class WXDLLIMPEXP_BASE wxFileTranslationsLoader
: 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
// ----------------------------------------------------------------------------

View File

@ -58,7 +58,7 @@ public:
Deletes previous loader and takes ownership of @a loader.
@see wxTranslationsLoader, wxFileTranslationsLoader
@see wxTranslationsLoader, wxFileTranslationsLoader, wxResourceTranslationsLoader
*/
void SetLoader(wxTranslationsLoader *loader);
@ -251,7 +251,7 @@ public:
Implementations must implement the LoadCatalog() method.
@see wxFileTranslationsLoader
@see wxFileTranslationsLoader, wxResourceTranslationsLoader
@since 2.9.1
*/
@ -308,6 +308,53 @@ public:
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;
};
// ============================================================================

View File

@ -809,13 +809,17 @@ WX_DECLARE_EXPORTED_STRING_HASH_MAP(wxString, wxMessagesHash);
class wxMsgCatalogFile
{
public:
typedef wxScopedCharTypeBuffer<char> DataBuffer;
// ctor & dtor
wxMsgCatalogFile();
~wxMsgCatalogFile();
// load the catalog from disk
bool Load(const wxString& filename,
wxPluralFormsCalculatorPtr& rPluralFormsCalculator);
bool LoadFile(const wxString& filename,
wxPluralFormsCalculatorPtr& rPluralFormsCalculator);
bool LoadData(const DataBuffer& data,
wxPluralFormsCalculatorPtr& rPluralFormsCalculator);
// fills the hash with string-translation pairs
bool FillHash(wxMessagesHash& hash, const wxString& msgIdCharset) const;
@ -847,7 +851,7 @@ private:
};
// all data is stored here
wxMemoryBuffer m_data;
DataBuffer m_data;
// data description
size_t32 m_numStrings; // number of strings in this domain
@ -865,25 +869,18 @@ private:
: 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 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_data.GetDataLen())
if ( ofsString + Swap(ent->nLen) > m_data.length())
{
return NULL;
}
return StringData() + ofsString;
return m_data.data() + ofsString;
}
bool m_bSwapped; // wrong endianness?
@ -908,9 +905,13 @@ public:
#endif
// load the catalog from disk
bool Load(const wxString& filename,
const wxString& domain,
const wxString& msgIdCharset);
bool LoadFile(const wxString& filename,
const wxString& domain,
const wxString& msgIdCharset);
bool LoadData(const wxScopedCharTypeBuffer<char>& data,
const wxString& domain,
const wxString& msgIdCharset);
// get name of the catalog
wxString GetDomain() const { return m_domain; }
@ -947,8 +948,8 @@ wxMsgCatalogFile::~wxMsgCatalogFile()
}
// open disk file and read in it's contents
bool wxMsgCatalogFile::Load(const wxString& filename,
wxPluralFormsCalculatorPtr& rPluralFormsCalculator)
bool wxMsgCatalogFile::LoadFile(const wxString& filename,
wxPluralFormsCalculatorPtr& rPluralFormsCalculator)
{
wxFile fileMsg(filename);
if ( !fileMsg.IsOpened() )
@ -962,17 +963,36 @@ bool wxMsgCatalogFile::Load(const wxString& filename,
size_t nSize = wx_truncate_cast(size_t, lenFile);
wxASSERT_MSG( nSize == lenFile + size_t(0), wxS("message catalog bigger than 4GB?") );
wxMemoryBuffer filedata;
// 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;
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
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 ) {
// we'll have to swap all the integers if it's true
m_bSwapped = pHeader->magic == MSGCATALOG_MAGIC_SW;
@ -983,16 +1003,17 @@ bool wxMsgCatalogFile::Load(const wxString& filename,
if ( !bValid ) {
// 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;
}
m_data = data;
// initialize
m_numStrings = Swap(pHeader->numStrings);
m_pOrigTable = (wxMsgTableEntry *)(StringData() +
m_pOrigTable = (wxMsgTableEntry *)(data.data() +
Swap(pHeader->ofsOrigTable));
m_pTransTable = (wxMsgTableEntry *)(StringData() +
m_pTransTable = (wxMsgTableEntry *)(data.data() +
Swap(pHeader->ofsTransTable));
// now parse catalog's header and try to extract catalog charset and
@ -1177,15 +1198,32 @@ wxMsgCatalog::~wxMsgCatalog()
}
#endif // !wxUSE_UNICODE
bool wxMsgCatalog::Load(const wxString& filename,
const wxString& domain,
const wxString& msgIdCharset)
bool wxMsgCatalog::LoadFile(const wxString& filename,
const wxString& domain,
const wxString& msgIdCharset)
{
wxMsgCatalogFile file;
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;
if ( !file.FillHash(m_messages, msgIdCharset) )
@ -1417,10 +1455,38 @@ bool wxTranslations::LoadCatalogFile(const wxString& filename,
wxMsgCatalog *pMsgCat = new wxMsgCatalog;
#if wxUSE_UNICODE
const bool ok = pMsgCat->Load(filename, domain, wxEmptyString/*unused*/);
const bool ok = pMsgCat->LoadFile(filename, domain, wxEmptyString/*unused*/);
#else
const bool ok = pMsgCat->Load(filename, domain,
m_msgIdCharset[domain]);
const bool ok = pMsgCat->LoadFile(filename, 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
if ( !ok )
@ -1728,6 +1794,41 @@ bool wxFileTranslationsLoader::LoadCatalog(wxTranslations *translations,
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)
// ----------------------------------------------------------------------------