From bc71c3cdd54e873e63fad71e37fbcd40c3c71feb Mon Sep 17 00:00:00 2001 From: =?utf8?q?V=C3=A1clav=20Slav=C3=ADk?= Date: Mon, 26 Apr 2010 20:51:22 +0000 Subject: [PATCH] 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 --- docs/changes.txt | 2 + include/wx/translation.h | 25 +++++- interface/wx/translation.h | 51 +++++++++++- src/common/translation.cpp | 165 ++++++++++++++++++++++++++++++------- 4 files changed, 208 insertions(+), 35 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index f465e50b0d..1bc9abedb6 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -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: diff --git a/include/wx/translation.h b/include/wx/translation.h index 3dcd97ab2a..de0e0d793a 100644 --- a/include/wx/translation.h +++ b/include/wx/translation.h @@ -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& 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 // ---------------------------------------------------------------------------- diff --git a/interface/wx/translation.h b/interface/wx/translation.h index cec4db149a..2416dca597 100644 --- a/interface/wx/translation.h +++ b/interface/wx/translation.h @@ -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; +}; + // ============================================================================ diff --git a/src/common/translation.cpp b/src/common/translation.cpp index 6ea7c334ac..79b6da756c 100644 --- a/src/common/translation.cpp +++ b/src/common/translation.cpp @@ -809,13 +809,17 @@ WX_DECLARE_EXPORTED_STRING_HASH_MAP(wxString, wxMessagesHash); class wxMsgCatalogFile { public: + typedef wxScopedCharTypeBuffer 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(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& 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& 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& 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(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) // ---------------------------------------------------------------------------- -- 2.45.2