]> git.saurik.com Git - wxWidgets.git/commitdiff
Add support for storing translations in win32 resources.
authorVáclav Slavík <vslavik@fastmail.fm>
Mon, 26 Apr 2010 20:51:22 +0000 (20:51 +0000)
committerVáclav Slavík <vslavik@fastmail.fm>
Mon, 26 Apr 2010 20:51:22 +0000 (20:51 +0000)
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@64155 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775

docs/changes.txt
include/wx/translation.h
interface/wx/translation.h
src/common/translation.cpp

index f465e50b0d381da3e7f99205e1c70768013e81a1..1bc9abedb63167611868eac2d9d1cc4a1f5b153a 100644 (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:
 
index 3dcd97ab2a96109784ec0c53e43337f8c54eb0a0..de0e0d793a2619fba553d396587f872dda09b3a2 100644 (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
 // ----------------------------------------------------------------------------
index cec4db149acb6271b3e3da0eda2cd7a213a6c2f4..2416dca5974c0b7df4879f2b7299cb0e2e7ffdf4 100644 (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;
+};
+
 
 
 // ============================================================================
index 6ea7c334ac17b6154774664449c02f13d7366313..79b6da756cc92cd22bdeca2333bd8453a3e2da0e 100644 (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)
 // ----------------------------------------------------------------------------