diff --git a/include/wx/translation.h b/include/wx/translation.h index 7c1e582b71..abd92a6d27 100644 --- a/include/wx/translation.h +++ b/include/wx/translation.h @@ -20,10 +20,9 @@ #include "wx/buffer.h" #include "wx/language.h" - -#if !wxUSE_UNICODE - #include "wx/hashmap.h" -#endif +#include "wx/hashmap.h" +#include "wx/strconv.h" +#include "wx/scopedptr.h" // ============================================================================ // global decls @@ -51,7 +50,58 @@ class WXDLLIMPEXP_FWD_BASE wxTranslationsLoader; class WXDLLIMPEXP_FWD_BASE wxLocale; -class wxMsgCatalog; + +class wxPluralFormsCalculator; +wxDECLARE_SCOPED_PTR(wxPluralFormsCalculator, wxPluralFormsCalculatorPtr) + +// ---------------------------------------------------------------------------- +// wxMsgCatalog corresponds to one loaded message catalog. +// ---------------------------------------------------------------------------- + +class WXDLLIMPEXP_BASE wxMsgCatalog +{ +public: + // load the catalog from disk or from data; caller is responsible for + // deleting them if not NULL + static wxMsgCatalog *CreateFromFile(const wxString& filename, + const wxString& domain); + + static wxMsgCatalog *CreateFromData(const wxScopedCharBuffer& data, + const wxString& domain); + + // get name of the catalog + wxString GetDomain() const { return m_domain; } + + // get the translated string: returns NULL if not found + const wxString *GetString(const wxString& sz, unsigned n = UINT_MAX) const; + +protected: + wxMsgCatalog(const wxString& domain) + : m_pNext(NULL), m_domain(domain) +#if !wxUSE_UNICODE + , m_conv(NULL) +#endif + {} +#if !wxUSE_UNICODE + ~wxMsgCatalog(); +#endif + +private: + // variable pointing to the next element in a linked list (or NULL) + wxMsgCatalog *m_pNext; + friend class wxTranslations; + + wxStringToStringHashMap m_messages; // all messages in the catalog + wxString m_domain; // name of the domain + +#if !wxUSE_UNICODE + // the conversion corresponding to this catalog charset if we installed it + // as the global one + wxCSConv *m_conv; +#endif + + wxPluralFormsCalculatorPtr m_pluralFormsCalculator; +}; // ---------------------------------------------------------------------------- // wxTranslations: message catalogs @@ -91,12 +141,6 @@ public: // check if the given catalog is loaded bool IsLoaded(const wxString& domain) const; - // 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, const wxString& domain = wxEmptyString) const; @@ -133,10 +177,6 @@ private: wxTranslationsLoader *m_loader; wxMsgCatalog *m_pMsgCat; // pointer to linked list of catalogs - -#if !wxUSE_UNICODE - wxStringToStringHashMap m_msgIdCharset; -#endif }; @@ -147,8 +187,8 @@ public: wxTranslationsLoader() {} virtual ~wxTranslationsLoader() {} - virtual bool LoadCatalog(wxTranslations *translations, - const wxString& domain, const wxString& lang) = 0; + virtual wxMsgCatalog *LoadCatalog(const wxString& domain, + const wxString& lang) = 0; }; @@ -159,8 +199,8 @@ class WXDLLIMPEXP_BASE wxFileTranslationsLoader public: static void AddCatalogLookupPathPrefix(const wxString& prefix); - virtual bool LoadCatalog(wxTranslations *translations, - const wxString& domain, const wxString& lang); + virtual wxMsgCatalog *LoadCatalog(const wxString& domain, + const wxString& lang); }; @@ -170,8 +210,8 @@ class WXDLLIMPEXP_BASE wxResourceTranslationsLoader : public wxTranslationsLoader { public: - virtual bool LoadCatalog(wxTranslations *translations, - const wxString& domain, const wxString& lang); + virtual wxMsgCatalog *LoadCatalog(const wxString& domain, + const wxString& lang); protected: // returns resource type to use for translations diff --git a/interface/wx/translation.h b/interface/wx/translation.h index 3e96b37b70..fc1c35a229 100644 --- a/interface/wx/translation.h +++ b/interface/wx/translation.h @@ -169,20 +169,6 @@ public: */ bool IsLoaded(const wxString& domain) const; - /** - Directly loads catalog from a file. - - It is caller's responsibility to ensure that the catalog contains - correct language. This function is primarily intended for - wxTranslationsLoader implementations. - - @param filename Name of the MO file to load. - @param domain Domain to load the translations into (typically - matches file's basename). - */ - bool LoadCatalogFile(const wxString& filename, - const wxString& domain = wxEmptyString); - /** Retrieves the translation for a string in all loaded domains unless the @a domain parameter is specified (and then only this catalog/domain is searched). @@ -264,10 +250,10 @@ public: /** Called to load requested catalog. - If the catalog is found, LoadCatalog() should call LoadCatalogFile() - on @a translations to add the translation. + If the catalog is found, LoadCatalog() should create wxMsgCatalog + instance with its data and return it. The caller will take ownership + of the catalog. - @param translations wxTranslations requesting loading. @param domain Domain to load. @param lang Language to look for. This is "canonical name" (see wxLocale::GetCanonicalName()), i.e. ISO 639 @@ -275,10 +261,10 @@ public: additional modifiers (e.g. "fr", "en_GB" or "ca@valencia"). - @return @true on successful load, @false otherwise + @return Loaded catalog or NULL on failure. */ - virtual bool LoadCatalog(wxTranslations *translations, - const wxString& domain, const wxString& lang) = 0; + virtual wxMsgCatalog *LoadCatalog(const wxString& domain, + const wxString& lang) = 0; }; /** @@ -335,7 +321,6 @@ public: This class is only available on Windows. @since 2.9.1 - */ class wxResourceTranslationsLoader : public wxTranslationsLoader { @@ -356,6 +341,42 @@ protected: }; +/** + Represents a loaded translations message catalog. + + This class should only be used directly by wxTranslationsLoader + implementations. + + @since 2.9.1 + */ +class wxMsgCatalog +{ +public: + /** + Creates catalog loaded from a MO file. + + @param filename Path to the MO file to load. + @param domain Catalog's domain. This typically matches + the @a filename. + + @return Successfully loaded catalog or NULL on failure. + */ + static wxMsgCatalog *CreateFromFile(const wxString& filename, + const wxString& domain); + + /** + Creates catalog from MO file data in memory buffer. + + @param data Data in MO file format. + @param domain Catalog's domain. This typically matches + the @a filename. + + @return Successfully loaded catalog or NULL on failure. + */ + static wxMsgCatalog *CreateFromData(const wxScopedCharBuffer& data, + const wxString& domain); +}; + // ============================================================================ // Global functions/macros diff --git a/src/common/translation.cpp b/src/common/translation.cpp index 5e259574d5..3aa38fc675 100644 --- a/src/common/translation.cpp +++ b/src/common/translation.cpp @@ -45,7 +45,6 @@ #include "wx/filename.h" #include "wx/tokenzr.h" #include "wx/fontmap.h" -#include "wx/scopedptr.h" #include "wx/stdpaths.h" #include "wx/hashset.h" @@ -69,6 +68,18 @@ const size_t32 MSGCATALOG_MAGIC_SW = 0xde120495; // implementation // ============================================================================ +namespace +{ + +#if !wxUSE_UNICODE +// We need to keep track of (char*) msgids in non-Unicode legacy builds. Instead +// of making the public wxMsgCatalog and wxTranslationsLoader APIs ugly, we +// store them in this global map. +wxStringToStringHashMap gs_msgIdCharset; +#endif + +} // anonymous namespace + // ---------------------------------------------------------------------------- // Plural forms parser // ---------------------------------------------------------------------------- @@ -414,7 +425,7 @@ private: wxPluralFormsNodePtr m_plural; }; -wxDEFINE_SCOPED_PTR_TYPE(wxPluralFormsCalculator) +wxDEFINE_SCOPED_PTR(wxPluralFormsCalculator, wxPluralFormsCalculatorPtr) void wxPluralFormsCalculator::init(wxPluralFormsToken::Number nplurals, wxPluralFormsNode* plural) @@ -785,12 +796,10 @@ wxPluralFormsCalculator* wxPluralFormsCalculator::make(const char* s) // http://www.gnu.org/software/autoconf/manual/gettext/MO-Files.html // ---------------------------------------------------------------------------- -WX_DECLARE_EXPORTED_STRING_HASH_MAP(wxString, wxMessagesHash); - class wxMsgCatalogFile { public: - typedef wxScopedCharTypeBuffer DataBuffer; + typedef wxScopedCharBuffer DataBuffer; // ctor & dtor wxMsgCatalogFile(); @@ -803,7 +812,7 @@ public: wxPluralFormsCalculatorPtr& rPluralFormsCalculator); // fills the hash with string-translation pairs - bool FillHash(wxMessagesHash& hash, const wxString& msgIdCharset) const; + bool FillHash(wxStringToStringHashMap& hash, const wxString& domain) const; // return the charset of the strings in this catalog or empty string if // none/unknown @@ -869,53 +878,6 @@ private: wxDECLARE_NO_COPY_CLASS(wxMsgCatalogFile); }; - -// ---------------------------------------------------------------------------- -// wxMsgCatalog corresponds to one loaded message catalog. -// -// This is a "low-level" class and is used only by wxLocale (that's why -// it's designed to be stored in a linked list) -// ---------------------------------------------------------------------------- - -class wxMsgCatalog -{ -public: -#if !wxUSE_UNICODE - wxMsgCatalog() { m_conv = NULL; } - ~wxMsgCatalog(); -#endif - - // load the catalog from disk - 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; } - - // get the translated string: returns NULL if not found - const wxString *GetString(const wxString& sz, unsigned n = UINT_MAX) const; - - // public variable pointing to the next element in a linked list (or NULL) - wxMsgCatalog *m_pNext; - -private: - wxMessagesHash m_messages; // all messages in the catalog - wxString m_domain; // name of the domain - -#if !wxUSE_UNICODE - // the conversion corresponding to this catalog charset if we installed it - // as the global one - wxCSConv *m_conv; -#endif - - wxPluralFormsCalculatorPtr m_pluralFormsCalculator; -}; - // ---------------------------------------------------------------------------- // wxMsgCatalogFile clas // ---------------------------------------------------------------------------- @@ -1057,10 +1019,10 @@ bool wxMsgCatalogFile::LoadData(const DataBuffer& data, return true; } -bool wxMsgCatalogFile::FillHash(wxMessagesHash& hash, - const wxString& msgIdCharset) const +bool wxMsgCatalogFile::FillHash(wxStringToStringHashMap& hash, + const wxString& domain) const { - wxUnusedVar(msgIdCharset); // silence warning in Unicode build + wxUnusedVar(domain); // silence warning in Unicode build // conversion to use to convert catalog strings to the GUI encoding wxMBConv *inputConv = NULL; @@ -1088,6 +1050,8 @@ bool wxMsgCatalogFile::FillHash(wxMessagesHash& hash, } #if !wxUSE_UNICODE + wxString msgIdCharset = gs_msgIdCharset[domain]; + // conversion to apply to msgid strings before looking them up: we only // need it if the msgids are neither in 7 bit ASCII nor in the same // encoding as the catalog @@ -1179,38 +1143,38 @@ wxMsgCatalog::~wxMsgCatalog() } #endif // !wxUSE_UNICODE -bool wxMsgCatalog::LoadFile(const wxString& filename, - const wxString& domain, - const wxString& msgIdCharset) +/* static */ +wxMsgCatalog *wxMsgCatalog::CreateFromFile(const wxString& filename, + const wxString& domain) { + wxScopedPtr cat(new wxMsgCatalog(domain)); + wxMsgCatalogFile file; - m_domain = domain; + if ( !file.LoadFile(filename, cat->m_pluralFormsCalculator) ) + return NULL; - if ( !file.LoadFile(filename, m_pluralFormsCalculator) ) - return false; + if ( !file.FillHash(cat->m_messages, domain) ) + return NULL; - if ( !file.FillHash(m_messages, msgIdCharset) ) - return false; - - return true; + return cat.release(); } -bool wxMsgCatalog::LoadData(const wxScopedCharTypeBuffer& data, - const wxString& domain, - const wxString& msgIdCharset) +/* static */ +wxMsgCatalog *wxMsgCatalog::CreateFromData(const wxScopedCharBuffer& data, + const wxString& domain) { + wxScopedPtr cat(new wxMsgCatalog(domain)); + wxMsgCatalogFile file; - m_domain = domain; + if ( !file.LoadData(data, cat->m_pluralFormsCalculator) ) + return NULL; - if ( !file.LoadData(data, m_pluralFormsCalculator) ) - return false; + if ( !file.FillHash(cat->m_messages, domain) ) + return NULL; - if ( !file.FillHash(m_messages, msgIdCharset) ) - return false; - - return true; + return cat.release(); } const wxString *wxMsgCatalog::GetString(const wxString& str, unsigned n) const @@ -1220,7 +1184,7 @@ const wxString *wxMsgCatalog::GetString(const wxString& str, unsigned n) const { index = m_pluralFormsCalculator->evaluate(n); } - wxMessagesHash::const_iterator i; + wxStringToStringHashMap::const_iterator i; if (index != 0) { i = m_messages.find(wxString(str) + wxChar(index)); // plural @@ -1349,7 +1313,7 @@ bool wxTranslations::AddCatalog(const wxString& domain, wxLanguage msgIdLanguage, const wxString& msgIdCharset) { - m_msgIdCharset[domain] = msgIdCharset; + gs_msgIdCharset[domain] = msgIdCharset; return AddCatalog(domain, msgIdLanguage); } #endif // !wxUSE_UNICODE @@ -1386,6 +1350,8 @@ bool wxTranslations::LoadCatalog(const wxString& domain, const wxString& lang) { wxCHECK_MSG( m_loader, false, "loader can't be NULL" ); + wxMsgCatalog *cat = NULL; + #if wxUSE_FONTMAP // first look for the catalog for this language and the current locale: // notice that we don't use the system name for the locale as this would @@ -1397,32 +1363,44 @@ bool wxTranslations::LoadCatalog(const wxString& domain, const wxString& lang) wxString fullname(lang); fullname << wxS('.') << wxFontMapperBase::GetEncodingName(encSys); - if ( m_loader->LoadCatalog(this, domain, fullname) ) - return true; + cat = m_loader->LoadCatalog(domain, fullname); } #endif // wxUSE_FONTMAP - // Next try: use the provided name language name: - if ( m_loader->LoadCatalog(this, domain, lang) ) - return true; - - // Also try just base locale name: for things like "fr_BE" (Belgium - // French) we should use fall back on plain "fr" if no Belgium-specific - // message catalogs exist - wxString baselang = lang.BeforeFirst('_'); - if ( lang != baselang ) + if ( !cat ) { - if ( m_loader->LoadCatalog(this, domain, baselang) ) - return true; + // Next try: use the provided name language name: + cat = m_loader->LoadCatalog(domain, lang); } - // Nothing worked, the catalog just isn't there - wxLogTrace(TRACE_I18N, - "Catalog \"%s.mo\" not found for language \"%s\".", - domain, lang); - return false; -} + if ( !cat ) + { + // Also try just base locale name: for things like "fr_BE" (Belgium + // French) we should use fall back on plain "fr" if no Belgium-specific + // message catalogs exist + wxString baselang = lang.BeforeFirst('_'); + if ( lang != baselang ) + cat = m_loader->LoadCatalog(domain, baselang); + } + if ( cat ) + { + // add it to the head of the list so that in GetString it will + // be searched before the catalogs added earlier + cat->m_pNext = m_pMsgCat; + m_pMsgCat = cat; + + return true; + } + else + { + // Nothing worked, the catalog just isn't there + wxLogTrace(TRACE_I18N, + "Catalog \"%s.mo\" not found for language \"%s\".", + domain, lang); + return false; + } +} // check if the given catalog is loaded bool wxTranslations::IsLoaded(const wxString& domain) const @@ -1431,62 +1409,6 @@ bool wxTranslations::IsLoaded(const wxString& domain) const } -bool wxTranslations::LoadCatalogFile(const wxString& filename, - const wxString& domain) -{ - wxMsgCatalog *pMsgCat = new wxMsgCatalog; - -#if wxUSE_UNICODE - const bool ok = pMsgCat->LoadFile(filename, domain, wxEmptyString/*unused*/); -#else - 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 ) - { - // 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; -} - - wxString wxTranslations::ChooseLanguageForDomain(const wxString& WXUNUSED(domain), const wxString& WXUNUSED(msgIdLang)) { @@ -1750,9 +1672,8 @@ void wxFileTranslationsLoader::AddCatalogLookupPathPrefix(const wxString& prefix } -bool wxFileTranslationsLoader::LoadCatalog(wxTranslations *translations, - const wxString& domain, - const wxString& lang) +wxMsgCatalog *wxFileTranslationsLoader::LoadCatalog(const wxString& domain, + const wxString& lang) { wxString searchPath = GetFullSearchPath(lang); @@ -1764,13 +1685,13 @@ bool wxFileTranslationsLoader::LoadCatalog(wxTranslations *translations, wxString strFullName; if ( !wxFindFileInPath(&strFullName, searchPath, fn.GetFullPath()) ) - return false; + return NULL; // open file and read its data wxLogVerbose(_("using catalog '%s' from '%s'."), domain, strFullName.c_str()); wxLogTrace(TRACE_I18N, wxS("Using catalog \"%s\"."), strFullName.c_str()); - return translations->LoadCatalogFile(strFullName, domain); + return wxMsgCatalog::CreateFromFile(strFullName, domain); } @@ -1779,9 +1700,8 @@ bool wxFileTranslationsLoader::LoadCatalog(wxTranslations *translations, // ---------------------------------------------------------------------------- #ifdef __WINDOWS__ -bool wxResourceTranslationsLoader::LoadCatalog(wxTranslations *translations, - const wxString& domain, - const wxString& lang) +wxMsgCatalog *wxResourceTranslationsLoader::LoadCatalog(const wxString& domain, + const wxString& lang) { const void *mo_data = NULL; @@ -1798,12 +1718,14 @@ bool wxResourceTranslationsLoader::LoadCatalog(wxTranslations *translations, 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 ) + wxMsgCatalog *cat = wxMsgCatalog::CreateFromData( + wxCharBuffer::CreateNonOwned(static_cast(mo_data), mo_size), + domain); + + if ( !cat ) wxLogWarning(_("Resource '%s' is not a valid message catalog."), resname); - return ok; + return cat; } #endif // __WINDOWS__