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:
parent
076c0a8ee8
commit
bc71c3cdd5
@ -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:
|
||||
|
||||
|
@ -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
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// ============================================================================
|
||||
|
@ -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)
|
||||
// ----------------------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user