+ wxMessagesHash::const_iterator i;
+ if (index != 0)
+ {
+ i = m_messages.find(wxString(str) + wxChar(index)); // plural
+ }
+ else
+ {
+ i = m_messages.find(str);
+ }
+
+ if ( i != m_messages.end() )
+ {
+ return &i->second;
+ }
+ else
+ return NULL;
+}
+
+
+// ----------------------------------------------------------------------------
+// wxTranslations
+// ----------------------------------------------------------------------------
+
+namespace
+{
+
+wxTranslations *gs_translations = NULL;
+bool gs_translationsOwned = false;
+
+} // anonymous namespace
+
+
+/*static*/
+wxTranslations *wxTranslations::Get()
+{
+ return gs_translations;
+}
+
+/*static*/
+void wxTranslations::Set(wxTranslations *t)
+{
+ if ( gs_translationsOwned )
+ delete gs_translations;
+ gs_translations = t;
+ gs_translationsOwned = true;
+}
+
+/*static*/
+void wxTranslations::SetNonOwned(wxTranslations *t)
+{
+ if ( gs_translationsOwned )
+ delete gs_translations;
+ gs_translations = t;
+ gs_translationsOwned = false;
+}
+
+
+wxTranslations::wxTranslations()
+{
+ m_pMsgCat = NULL;
+ m_loader = new wxFileTranslationsLoader;
+}
+
+
+wxTranslations::~wxTranslations()
+{
+ delete m_loader;
+
+ // free catalogs memory
+ wxMsgCatalog *pTmpCat;
+ while ( m_pMsgCat != NULL )
+ {
+ pTmpCat = m_pMsgCat;
+ m_pMsgCat = m_pMsgCat->m_pNext;
+ delete pTmpCat;
+ }
+}
+
+
+void wxTranslations::SetLoader(wxTranslationsLoader *loader)
+{
+ wxCHECK_RET( loader, "loader can't be NULL" );
+
+ delete m_loader;
+ m_loader = loader;
+}
+
+
+void wxTranslations::SetLanguage(wxLanguage lang)
+{
+ if ( lang == wxLANGUAGE_DEFAULT )
+ SetLanguage("");
+ else
+ SetLanguage(wxLocale::GetLanguageCanonicalName(lang));
+}
+
+void wxTranslations::SetLanguage(const wxString& lang)
+{
+ m_lang = lang;
+}
+
+
+bool wxTranslations::AddStdCatalog()
+{
+ if ( !AddCatalog(wxS("wxstd")) )
+ return false;
+
+ // there may be a catalog with toolkit specific overrides, it is not
+ // an error if this does not exist
+ wxString port(wxPlatformInfo::Get().GetPortIdName());
+ if ( !port.empty() )
+ {
+ AddCatalog(port.BeforeFirst(wxS('/')).MakeLower());
+ }
+
+ return true;
+}
+
+
+bool wxTranslations::AddCatalog(const wxString& domain)
+{
+ return AddCatalog(domain, wxLANGUAGE_ENGLISH_US);
+}
+
+#if !wxUSE_UNICODE
+bool wxTranslations::AddCatalog(const wxString& domain,
+ wxLanguage msgIdLanguage,
+ const wxString& msgIdCharset)
+{
+ m_msgIdCharset[domain] = msgIdCharset;
+ return AddCatalog(domain, msgIdLanguage);
+}
+#endif // !wxUSE_UNICODE
+
+bool wxTranslations::AddCatalog(const wxString& domain,
+ wxLanguage msgIdLanguage)
+{
+ const wxString msgIdLang = wxLocale::GetLanguageCanonicalName(msgIdLanguage);
+ const wxString domain_lang = ChooseLanguageForDomain(domain, msgIdLang);
+
+ if ( domain_lang.empty() )
+ {
+ wxLogTrace(TRACE_I18N,
+ wxS("no suitable translation for domain '%s' found"),
+ domain);
+ return false;
+ }
+
+ wxLogTrace(TRACE_I18N,
+ wxS("adding '%s' translation for domain '%s' (msgid language '%s')"),
+ domain_lang, domain, msgIdLang);
+
+ // It is OK to not load catalog if the msgid language and m_language match,
+ // in which case we can directly display the texts embedded in program's
+ // source code:
+ if ( msgIdLang == domain_lang )
+ return true;
+
+ wxCHECK_MSG( m_loader, false, "loader can't be NULL" );
+ return m_loader->LoadCatalog(this, domain, domain_lang);
+}
+
+
+// check if the given catalog is loaded
+bool wxTranslations::IsLoaded(const wxString& domain) const
+{
+ return FindCatalog(domain) != NULL;
+}
+
+
+bool wxTranslations::LoadCatalogFile(const wxString& filename,
+ const wxString& domain)
+{
+ wxMsgCatalog *pMsgCat = new wxMsgCatalog;
+
+#if wxUSE_UNICODE
+ const bool ok = pMsgCat->Load(filename, domain, wxEmptyString/*unused*/);
+#else
+ const bool ok = pMsgCat->Load(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;
+}
+
+
+wxString wxTranslations::ChooseLanguageForDomain(const wxString& WXUNUSED(domain),
+ const wxString& WXUNUSED(msgIdLang))
+{
+ // explicitly set language should always be respected
+ if ( !m_lang.empty() )
+ return m_lang;
+
+ // TODO: if the default language is used, pick the best (by comparing
+ // available languages with user's preferences), instead of blindly
+ // trusting availability of system language translation
+ return wxLocale::GetLanguageCanonicalName(wxLocale::GetSystemLanguage());
+}
+
+
+namespace
+{
+WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual,
+ wxLocaleUntranslatedStrings);
+}
+
+/* static */
+const wxString& wxTranslations::GetUntranslatedString(const wxString& str)
+{
+ static wxLocaleUntranslatedStrings s_strings;
+
+ wxLocaleUntranslatedStrings::iterator i = s_strings.find(str);
+ if ( i == s_strings.end() )
+ return *s_strings.insert(str).first;
+
+ return *i;
+}
+
+
+const wxString& wxTranslations::GetString(const wxString& origString,
+ const wxString& domain) const
+{
+ return GetString(origString, origString, size_t(-1), domain);
+}
+
+const wxString& wxTranslations::GetString(const wxString& origString,
+ const wxString& origString2,
+ size_t n,
+ const wxString& domain) const
+{
+ if ( origString.empty() )
+ return GetUntranslatedString(origString);
+
+ const wxString *trans = NULL;
+ wxMsgCatalog *pMsgCat;
+
+ if ( !domain.empty() )
+ {
+ pMsgCat = FindCatalog(domain);
+
+ // does the catalog exist?
+ if ( pMsgCat != NULL )
+ trans = pMsgCat->GetString(origString, n);
+ }
+ else
+ {
+ // search in all domains
+ for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext )
+ {
+ trans = pMsgCat->GetString(origString, n);
+ if ( trans != NULL ) // take the first found
+ break;
+ }
+ }
+
+ if ( trans == NULL )
+ {
+ wxLogTrace
+ (
+ TRACE_I18N,
+ "string \"%s\"%s not found in %slocale '%s'.",
+ origString,
+ ((long)n) != -1 ? wxString::Format("[%ld]", (long)n) : wxString(),
+ !domain.empty() ? wxString::Format("domain '%s' ", domain) : wxString(),
+ m_lang
+ );
+
+ if (n == size_t(-1))
+ return GetUntranslatedString(origString);
+ else
+ return GetUntranslatedString(n == 1 ? origString : origString2);
+ }
+
+ return *trans;
+}
+
+
+wxString wxTranslations::GetHeaderValue(const wxString& header,
+ const wxString& domain) const
+{
+ if ( header.empty() )
+ return wxEmptyString;
+
+ const wxString *trans = NULL;
+ wxMsgCatalog *pMsgCat;
+
+ if ( !domain.empty() )
+ {
+ pMsgCat = FindCatalog(domain);
+
+ // does the catalog exist?
+ if ( pMsgCat == NULL )
+ return wxEmptyString;
+
+ trans = pMsgCat->GetString(wxEmptyString, (size_t)-1);
+ }
+ else
+ {
+ // search in all domains
+ for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext )
+ {
+ trans = pMsgCat->GetString(wxEmptyString, (size_t)-1);
+ if ( trans != NULL ) // take the first found
+ break;
+ }
+ }
+
+ if ( !trans || trans->empty() )
+ return wxEmptyString;
+
+ size_t found = trans->find(header);
+ if ( found == wxString::npos )
+ return wxEmptyString;
+
+ found += header.length() + 2 /* ': ' */;
+
+ // Every header is separated by \n
+
+ size_t endLine = trans->find(wxS('\n'), found);
+ size_t len = (endLine == wxString::npos) ?
+ wxString::npos : (endLine - found);
+
+ return trans->substr(found, len);
+}
+
+
+// find catalog by name in a linked list, return NULL if !found
+wxMsgCatalog *wxTranslations::FindCatalog(const wxString& domain) const
+{
+ // linear search in the linked list
+ wxMsgCatalog *pMsgCat;
+ for ( pMsgCat = m_pMsgCat; pMsgCat != NULL; pMsgCat = pMsgCat->m_pNext )
+ {
+ if ( pMsgCat->GetDomain() == domain )
+ return pMsgCat;
+ }
+
+ return NULL;
+}
+
+// ----------------------------------------------------------------------------
+// wxFileTranslationsLoader
+// ----------------------------------------------------------------------------
+
+namespace
+{
+
+// the list of the directories to search for message catalog files
+wxArrayString gs_searchPrefixes;
+
+// return the directories to search for message catalogs under the given
+// prefix, separated by wxPATH_SEP
+wxString GetMsgCatalogSubdirs(const wxString& prefix, const wxString& lang)
+{
+ // Search first in Unix-standard prefix/lang/LC_MESSAGES, then in
+ // prefix/lang and finally in just prefix.
+ //
+ // Note that we use LC_MESSAGES on all platforms and not just Unix, because
+ // it doesn't cost much to look into one more directory and doing it this
+ // way has two important benefits:
+ // a) we don't break compatibility with wx-2.6 and older by stopping to
+ // look in a directory where the catalogs used to be and thus silently
+ // breaking apps after they are recompiled against the latest wx
+ // b) it makes it possible to package app's support files in the same
+ // way on all target platforms
+ const wxString pathPrefix = wxFileName(prefix, lang).GetFullPath();
+
+ wxString searchPath;
+ searchPath.reserve(4*pathPrefix.length());
+ searchPath << pathPrefix << wxFILE_SEP_PATH << "LC_MESSAGES" << wxPATH_SEP
+ << prefix << wxFILE_SEP_PATH << wxPATH_SEP
+ << pathPrefix;
+
+ return searchPath;
+}
+
+// construct the search path for the given language
+static wxString GetFullSearchPath(const wxString& lang)
+{
+ // first take the entries explicitly added by the program
+ wxArrayString paths;
+ paths.reserve(gs_searchPrefixes.size() + 1);
+ size_t n,
+ count = gs_searchPrefixes.size();
+ for ( n = 0; n < count; n++ )
+ {
+ paths.Add(GetMsgCatalogSubdirs(gs_searchPrefixes[n], lang));
+ }
+
+
+#if wxUSE_STDPATHS
+ // then look in the standard location
+ const wxString stdp = wxStandardPaths::Get().
+ GetLocalizedResourcesDir(lang, wxStandardPaths::ResourceCat_Messages);
+
+ if ( paths.Index(stdp) == wxNOT_FOUND )
+ paths.Add(stdp);
+#endif // wxUSE_STDPATHS
+
+ // last look in default locations
+#ifdef __UNIX__
+ // LC_PATH is a standard env var containing the search path for the .mo
+ // files
+ const char *pszLcPath = wxGetenv("LC_PATH");
+ if ( pszLcPath )
+ {
+ const wxString lcp = GetMsgCatalogSubdirs(pszLcPath, lang);
+ if ( paths.Index(lcp) == wxNOT_FOUND )
+ paths.Add(lcp);
+ }
+
+ // also add the one from where wxWin was installed:
+ wxString wxp = wxGetInstallPrefix();
+ if ( !wxp.empty() )
+ {
+ wxp = GetMsgCatalogSubdirs(wxp + wxS("/share/locale"), lang);
+ if ( paths.Index(wxp) == wxNOT_FOUND )
+ paths.Add(wxp);
+ }
+#endif // __UNIX__
+
+
+ // finally construct the full search path
+ wxString searchPath;
+ searchPath.reserve(500);
+ count = paths.size();
+ for ( n = 0; n < count; n++ )
+ {
+ searchPath += paths[n];
+ if ( n != count - 1 )
+ searchPath += wxPATH_SEP;
+ }
+
+ return searchPath;
+}
+
+} // anonymous namespace
+
+
+void wxFileTranslationsLoader::AddCatalogLookupPathPrefix(const wxString& prefix)
+{
+ if ( gs_searchPrefixes.Index(prefix) == wxNOT_FOUND )
+ {
+ gs_searchPrefixes.Add(prefix);
+ }
+ //else: already have it
+}
+
+
+bool wxFileTranslationsLoader::LoadCatalog(wxTranslations *translations,
+ const wxString& domain,
+ const wxString& lang)
+{
+ wxCHECK_MSG( lang.length() >= LEN_LANG, false,
+ "invalid language specification" );
+
+ wxString searchPath;
+
+#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
+ // force us to install catalogs in different locations depending on the
+ // system but always use the canonical name
+ wxFontEncoding encSys = wxLocale::GetSystemEncoding();
+ if ( encSys != wxFONTENCODING_SYSTEM )