#include "wx/defs.h"
#include "wx/string.h"
+#if !wxUSE_UNICODE
+ #include "wx/hashmap.h"
+#endif
+
// Make wxLayoutDirection enum available without need for wxUSE_INTL so wxWindow, wxApp
// and other classes are not distrubed by wxUSE_INTL
// forward decls
// ----------------------------------------------------------------------------
+class WXDLLIMPEXP_FWD_BASE wxTranslationsLoader;
class WXDLLIMPEXP_FWD_BASE wxLocale;
class WXDLLIMPEXP_FWD_BASE wxLanguageInfoArray;
class wxMsgCatalog;
inline wxString wxLanguageInfo::GetLocaleName() const { return CanonicalName; }
#endif // !__WXMSW__
+// ----------------------------------------------------------------------------
+// wxTranslations: message catalogs
+// ----------------------------------------------------------------------------
+
+// this class allows to get translations for strings
+class WXDLLIMPEXP_BASE wxTranslations
+{
+public:
+ wxTranslations();
+ ~wxTranslations();
+
+ // returns current translations object, may return NULL
+ static wxTranslations *Get();
+ // sets current translations object (takes ownership; may be NULL)
+ static void Set(wxTranslations *t);
+
+ // changes loader to non-default one; takes ownership of 'loader'
+ void SetLoader(wxTranslationsLoader *loader);
+
+ void SetLanguage(wxLanguage lang);
+ void SetLanguage(const wxString& lang);
+
+ // add standard wxWidgets catalog ("wxstd")
+ bool AddStdCatalog();
+
+ // add catalog with given domain name and language, looking it up via
+ // wxTranslationsLoader
+ bool AddCatalog(const wxString& domain);
+ bool AddCatalog(const wxString& domain, wxLanguage msgIdLanguage);
+#if !wxUSE_UNICODE
+ bool AddCatalog(const wxString& domain,
+ wxLanguage msgIdLanguage,
+ const wxString& msgIdCharset);
+#endif
+
+ // check if the given catalog is loaded
+ bool IsLoaded(const wxString& domain) const;
+
+ // load catalog data directly from file
+ bool LoadCatalogFile(const wxString& filename,
+ const wxString& domain = wxEmptyString);
+
+ // access to translations
+ const wxString& GetString(const wxString& origString,
+ const wxString& domain = wxEmptyString) const;
+ const wxString& GetString(const wxString& origString,
+ const wxString& origString2,
+ size_t n,
+ const wxString& domain = wxEmptyString) const;
+
+ wxString GetHeaderValue(const wxString& header,
+ const wxString& domain = wxEmptyString) const;
+
+ // this is hack to work around a problem with wxGetTranslation() which
+ // returns const wxString& and not wxString, so when it returns untranslated
+ // string, it needs to have a copy of it somewhere
+ static const wxString& GetUntranslatedString(const wxString& str);
+
+private:
+ // find best translation for given domain
+ wxString ChooseLanguageForDomain(const wxString& domain,
+ const wxString& msgIdLang);
+
+ // find catalog by name in a linked list, return NULL if !found
+ wxMsgCatalog *FindCatalog(const wxString& domain) const;
+
+ // same as Set(), without taking ownership; only for wxLocale
+ static void SetNonOwned(wxTranslations *t);
+ friend class wxLocale;
+
+private:
+ wxString m_lang;
+ wxTranslationsLoader *m_loader;
+
+ wxMsgCatalog *m_pMsgCat; // pointer to linked list of catalogs
+
+#if !wxUSE_UNICODE
+ wxStringToStringHashMap m_msgIdCharset;
+#endif
+};
+
+
+// abstraction of translations discovery and loading
+class WXDLLIMPEXP_BASE wxTranslationsLoader
+{
+public:
+ wxTranslationsLoader() {}
+ virtual ~wxTranslationsLoader() {}
+
+ virtual bool LoadCatalog(wxTranslations *translations,
+ const wxString& domain, const wxString& lang) = 0;
+};
+
+// standard wxTranslationsLoader implementation, using filesystem
+class WXDLLIMPEXP_BASE wxFileTranslationsLoader
+ : public wxTranslationsLoader
+{
+public:
+ static void AddCatalogLookupPathPrefix(const wxString& prefix);
+
+ virtual bool LoadCatalog(wxTranslations *translations,
+ const wxString& domain, const wxString& lang);
+};
+
+
// ----------------------------------------------------------------------------
// wxLocaleCategory: the category of locale settings
// ----------------------------------------------------------------------------
// (in this order).
//
// This only applies to subsequent invocations of AddCatalog()!
- static void AddCatalogLookupPathPrefix(const wxString& prefix);
+ static void AddCatalogLookupPathPrefix(const wxString& prefix)
+ { wxFileTranslationsLoader::AddCatalogLookupPathPrefix(prefix); }
// add a catalog: it's searched for in standard places (current directory
// first, system one after), but the you may prepend additional directories to
// The loaded catalog will be used for message lookup by GetString().
//
// Returns 'true' if it was successfully loaded
- bool AddCatalog(const wxString& domain);
+ bool AddCatalog(const wxString& domain)
+ { return m_translations.AddCatalog(domain); }
+ bool AddCatalog(const wxString& domain, wxLanguage msgIdLanguage)
+ { return m_translations.AddCatalog(domain, msgIdLanguage); }
bool AddCatalog(const wxString& domain,
wxLanguage msgIdLanguage, const wxString& msgIdCharset);
static bool IsAvailable(int lang);
// check if the given catalog is loaded
- bool IsLoaded(const wxString& domain) const;
+ bool IsLoaded(const wxString& domain) const
+ { return m_translations.IsLoaded(domain); }
// Retrieve the language info struct for the given language
//
// is not in database
static wxString GetLanguageName(int lang);
+ // Returns ISO code ("canonical name") of language or empty string if the
+ // language is not in database
+ static wxString GetLanguageCanonicalName(int lang);
+
// Find the language for the given locale string which may be either a
// canonical ISO 2 letter language code ("xx"), a language code followed by
// the country code ("xx_XX") or a Windows full language name ("Xxxxx...")
//
// domains are searched in the last to first order, i.e. catalogs
// added later override those added before.
- virtual const wxString& GetString(const wxString& origString,
- const wxString& domain = wxEmptyString) const;
+ const wxString& GetString(const wxString& origString,
+ const wxString& domain = wxEmptyString) const
+ {
+ return m_translations.GetString(origString, domain);
+ }
// plural form version of the same:
- virtual const wxString& GetString(const wxString& origString,
- const wxString& origString2,
- size_t n,
- const wxString& domain = wxEmptyString) const;
+ const wxString& GetString(const wxString& origString,
+ const wxString& origString2,
+ size_t n,
+ const wxString& domain = wxEmptyString) const
+ {
+ return m_translations.GetString(origString, origString2, n, domain);
+ }
// this is hack to work around a problem with wxGetTranslation() which
// returns const wxString& and not wxString, so when it returns untranslated
// string, it needs to have a copy of it somewhere
- static const wxString& GetUntranslatedString(const wxString& str);
+ static const wxString& GetUntranslatedString(const wxString& str)
+ { return wxTranslations::GetUntranslatedString(str); }
// Returns the current short name for the locale
const wxString& GetName() const { return m_strShort; }
// return the contents of .po file header
wxString GetHeaderValue(const wxString& header,
- const wxString& domain = wxEmptyString) const;
+ const wxString& domain = wxEmptyString) const
+ {
+ return m_translations.GetHeaderValue(header, domain);
+ }
// These two methods are for internal use only. First one creates
// ms_languagesDB if it doesn't already exist, second one destroys
static void DestroyLanguagesDB();
private:
- // find catalog by name in a linked list, return NULL if !found
- wxMsgCatalog *FindCatalog(const wxString& domain) const;
+ bool DoInit(const wxString& name,
+ const wxString& shortName,
+ const wxString& locale);
// copy default table of languages from global static array to
// m_langugagesInfo, called by InitLanguagesDB
const char *m_pszOldLocale; // previous locale from setlocale()
wxLocale *m_pOldLocale; // previous wxLocale
- wxMsgCatalog *m_pMsgCat; // pointer to linked list of catalogs
-
bool m_initialized;
+ wxTranslations m_translations;
+
static wxLanguageInfoArray *ms_languagesDB;
wxDECLARE_NO_COPY_CLASS(wxLocale);
inline const wxString& wxGetTranslation(const wxString& str,
const wxString& domain = wxEmptyString)
{
- wxLocale *pLoc = wxGetLocale();
- if (pLoc)
- return pLoc->GetString(str, domain);
+ wxTranslations *trans = wxTranslations::Get();
+ if ( trans )
+ return trans->GetString(str, domain);
else
// NB: this function returns reference to a string, so we have to keep
// a copy of it somewhere
- return wxLocale::GetUntranslatedString(str);
+ return wxTranslations::GetUntranslatedString(str);
}
inline const wxString& wxGetTranslation(const wxString& str1,
const wxString& str2,
size_t n,
const wxString& domain = wxEmptyString)
{
- wxLocale *pLoc = wxGetLocale();
- if (pLoc)
- return pLoc->GetString(str1, str2, n, domain);
+ wxTranslations *trans = wxTranslations::Get();
+ if ( trans )
+ return trans->GetString(str1, str2, n, domain);
else
// NB: this function returns reference to a string, so we have to keep
// a copy of it somewhere
return n == 1
- ? wxLocale::GetUntranslatedString(str1)
- : wxLocale::GetUntranslatedString(str2);
+ ? wxTranslations::GetUntranslatedString(str1)
+ : wxTranslations::GetUntranslatedString(str2);
}
#else // !wxUSE_INTL
wxLocale class encapsulates all language-dependent settings and is a
generalization of the C locale concept.
- In wxWidgets this class manages message catalogs which contain the translations
- of the strings used to the current language.
+ In wxWidgets this class manages current locale. It also initializes and
+ activates wxTranslations object that manages message catalogs.
For a list of the supported languages, please see ::wxLanguage enum values.
These constants may be used to specify the language in wxLocale::Init and
@library{wxbase}
@category{cfg}
- @see @ref overview_i18n, @ref page_samples_internat, wxXLocale
+ @see @ref overview_i18n, @ref page_samples_internat, wxXLocale, wxTranslations
*/
class wxLocale
{
virtual ~wxLocale();
/**
- Add a catalog for use with the current locale: it is searched for in standard
- places (current directory first, then the system one), but you may also prepend
- additional directories to the search path with AddCatalogLookupPathPrefix().
-
- All loaded catalogs will be used for message lookup by GetString() for
- the current locale.
-
- In this overload, @c msgid strings are assumed
- to be in English and written only using 7-bit ASCII characters.
- If you have to deal with non-English strings or 8-bit characters in the
- source code, see the instructions in @ref overview_nonenglish.
-
- @return
- @true if catalog was successfully loaded, @false otherwise (which might
- mean that the catalog is not found or that it isn't in the correct format).
+ Calls wxTranslations::AddCatalog(const wxString&).
*/
bool AddCatalog(const wxString& domain);
/**
- Add a catalog for use with the current locale: it is searched for in standard
- places (current directory first, then the system one), but you may also prepend
- additional directories to the search path with AddCatalogLookupPathPrefix().
-
- All loaded catalogs will be used for message lookup by GetString() for
- the current locale.
-
- This overload takes two additional arguments, @a msgIdLanguage and @a msgIdCharset.
-
- @param domain
- The catalog domain to add.
-
- @param msgIdLanguage
- Specifies the language of "msgid" strings in source code
- (i.e. arguments to GetString(), wxGetTranslation() and the _() macro).
- It is used if AddCatalog() cannot find any catalog for current language:
- if the language is same as source code language, then strings from source
- code are used instead.
-
- @param msgIdCharset
- Lets you specify the charset used for msgids in sources
- in case they use 8-bit characters (e.g. German or French strings).
- This argument has no effect in Unicode build, because literals in sources are
- Unicode strings; you have to use compiler-specific method of setting the right
- charset when compiling with Unicode.
+ Calls wxTranslations::AddCatalog(const wxString&, wxLanguage).
+ */
+ bool AddCatalog(const wxString& domain, wxLanguage msgIdLanguage);
- @return
- @true if catalog was successfully loaded, @false otherwise (which might
- mean that the catalog is not found or that it isn't in the correct format).
+ /**
+ Calls wxTranslations::AddCatalog(const wxString&, wxLanguage, const wxString&).
*/
bool AddCatalog(const wxString& domain, wxLanguage msgIdLanguage,
const wxString& msgIdCharset);
/**
- Add a prefix to the catalog lookup path: the message catalog files will
- be looked up under prefix/lang/LC_MESSAGES, prefix/lang and prefix
- (in this order).
-
- This only applies to subsequent invocations of AddCatalog().
+ Calls wxFileTranslationsLoader::AddCatalogLookupPathPrefix().
*/
static void AddCatalogLookupPathPrefix(const wxString& prefix);
wxString GetCanonicalName() const;
/**
- Returns the header value for header @a header.
- The search for @a header is case sensitive. If an @a domain is passed,
- this domain is searched. Else all domains will be searched until a
- header has been found.
-
- The return value is the value of the header if found. Else this will be empty.
+ Calls wxTranslations::GetHeaderValue().
*/
wxString GetHeaderValue(const wxString& header,
const wxString& domain = wxEmptyString) const;
*/
static wxString GetLanguageName(int lang);
+ /**
+ Returns canonical name (see GetCanonicalName()) of the given language
+ or empty string if this language is unknown.
+
+ See GetLanguageInfo() for a remark about special meaning of @c wxLANGUAGE_DEFAULT.
+
+ @since 2.9.1
+ */
+ static wxString GetLanguageCanonicalName(int lang);
+
/**
Returns the locale name as passed to the constructor or Init().
const wxString& GetName() const;
/**
- 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).
-
- Returns original string if translation is not available (in this case an
- error message is generated the first time a string is not found; use
- wxLogNull to suppress it).
-
- @remarks Domains are searched in the last to first order, i.e. catalogs
- added later override those added before.
+ Calls wxTranslations::GetString(const wxString&, const wxString&) const.
*/
virtual const wxString& GetString(const wxString& origString,
const wxString& domain = wxEmptyString) const;
/**
- 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).
-
- Returns original string if translation is not available (in this case an
- error message is generated the first time a string is not found; use
- wxLogNull to suppress it).
-
- This form is used when retrieving translation of string that has different
- singular and plural form in English or different plural forms in some
- other language.
- It takes two extra arguments: @a origString parameter must contain the
- singular form of the string to be converted.
-
- It is also used as the key for the search in the catalog.
- The @a origString2 parameter is the plural form (in English).
-
- The parameter @a n is used to determine the plural form.
- If no message catalog is found @a origString is returned if 'n == 1',
- otherwise @a origString2.
-
- See GNU gettext manual for additional information on plural forms handling.
- This method is called by the wxGetTranslation() function and _() macro.
-
- @remarks Domains are searched in the last to first order, i.e. catalogs
- added later override those added before.
+ Calls wxTranslations::GetString(const wxString&, const wxString&, size_t, const wxString&) const.
*/
virtual const wxString& GetString(const wxString& origString,
const wxString& origString2, size_t n,
*/
static bool IsAvailable(int lang);
+ /**
+ Calls wxTranslations::IsLoaded().
+ */
+ bool IsLoaded(const wxString& domain) const;
+
+ /**
+ Returns @true if the locale could be set successfully.
+ */
+ bool IsOk() const;
+};
+
+
+/**
+ This class allows to get translations for strings.
+
+ In wxWidgets this class manages message catalogs which contain the
+ translations of the strings used to the current language. Unlike wxLocale,
+ it isn't bound to locale. It can be used either independently of, or in
+ conjunction with wxLocale. In the latter case, you should initialize
+ wxLocale (which creates wxTranslations instance) first; in the former, you
+ need to create a wxTranslations object and Set() it manually.
+
+ Only one wxTranslations instance is active at a time; it is set with the
+ Set() method and obtained using Get().
+
+ Unlike wxLocale, wxTranslations' primary mean of identifying language
+ is by its "canonical name", i.e. ISO 639 code, possibly combined with
+ ISO 3166 country code and additional modifiers (examples include
+ "fr", "en_GB" or "ca@valencia"; see wxLocale::GetCanonicalName() for
+ more information). This allows apps using wxTranslations API to use even
+ languages not recognized by the operating system or not listed in
+ wxLanguage enum.
+
+ @since 2.9.1
+
+ @see wxLocale
+ */
+class wxTranslations
+{
+public:
+ /// Constructor
+ wxTranslations();
+
+ /**
+ Returns current translations object, may return NULL.
+
+ You must either call this early in app initialization code, or let
+ wxLocale do it for you.
+ */
+ static wxTranslations *Get();
+
+ /**
+ Sets current translations object.
+
+ Deletes previous translation object and takes ownership of @a t.
+ */
+ static void Set(wxTranslations *t);
+
+ /**
+ Changes loader use to read catalogs to a non-default one.
+
+ Deletes previous loader and takes ownership of @a loader.
+
+ @see wxTranslationsLoader, wxFileTranslationsLoader
+ */
+ void SetLoader(wxTranslationsLoader *loader);
+
+ /**
+ Sets translations language to use.
+
+ wxLANGUAGE_DEFAULT has special meaning: best suitable translation,
+ given user's preference and available translations, will be used.
+ */
+ void SetLanguage(wxLanguage lang);
+
+ /**
+ Sets translations language to use.
+
+ Empty @a lang string has the same meaning as wxLANGUAGE_DEFAULT in
+ SetLanguage(wxLanguage): best suitable translation, given user's
+ preference and available translations, will be used.
+ */
+ void SetLanguage(const wxString& lang);
+
+ /**
+ Add standard wxWidgets catalogs ("wxstd" and possible port-specific
+ catalogs).
+
+ @return @true if a suitable catalog was found, @false otherwise
+
+ @see AddCatalog()
+ */
+ bool AddStdCatalog();
+
+ /**
+ Add a catalog for use with the current locale.
+
+ By default, it is searched for in standard places (see
+ wxFileTranslationsLoader), but you may also prepend additional
+ directories to the search path with
+ wxFileTranslationsLoader::AddCatalogLookupPathPrefix().
+
+ All loaded catalogs will be used for message lookup by GetString() for
+ the current locale.
+
+ In this overload, @c msgid strings are assumed
+ to be in English and written only using 7-bit ASCII characters.
+ If you have to deal with non-English strings or 8-bit characters in the
+ source code, see the instructions in @ref overview_nonenglish.
+
+ @return
+ @true if catalog was successfully loaded, @false otherwise (which might
+ mean that the catalog is not found or that it isn't in the correct format).
+ */
+ bool AddCatalog(const wxString& domain);
+
+ /**
+ Same as AddCatalog(const wxString&), but takes an additional argument,
+ @a msgIdLanguage.
+
+ @param domain
+ The catalog domain to add.
+
+ @param msgIdLanguage
+ Specifies the language of "msgid" strings in source code
+ (i.e. arguments to GetString(), wxGetTranslation() and the _() macro).
+ It is used if AddCatalog() cannot find any catalog for current language:
+ if the language is same as source code language, then strings from source
+ code are used instead.
+
+ @return
+ @true if catalog was successfully loaded, @false otherwise (which might
+ mean that the catalog is not found or that it isn't in the correct format).
+ */
+ bool AddCatalog(const wxString& domain, wxLanguage msgIdLanguage);
+
+ /**
+ Same as AddCatalog(const wxString&, wxLanguage), but takes two
+ additional arguments, @a msgIdLanguage and @a msgIdCharset.
+
+ This overload is only available in non-Unicode build.
+
+ @param domain
+ The catalog domain to add.
+
+ @param msgIdLanguage
+ Specifies the language of "msgid" strings in source code
+ (i.e. arguments to GetString(), wxGetTranslation() and the _() macro).
+ It is used if AddCatalog() cannot find any catalog for current language:
+ if the language is same as source code language, then strings from source
+ code are used instead.
+
+ @param msgIdCharset
+ Lets you specify the charset used for msgids in sources
+ in case they use 8-bit characters (e.g. German or French strings).
+
+ @return
+ @true if catalog was successfully loaded, @false otherwise (which might
+ mean that the catalog is not found or that it isn't in the correct format).
+ */
+ bool AddCatalog(const wxString& domain,
+ wxLanguage msgIdLanguage,
+ const wxString& msgIdCharset);
+
/**
Check if the given catalog is loaded, and returns @true if it is.
'domain' which is more or less the application name.
@see AddCatalog()
- */
+ */
bool IsLoaded(const wxString& domain) const;
/**
- Returns @true if the locale could be set successfully.
+ 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).
+
+ Returns original string if translation is not available (in this case an
+ error message is generated the first time a string is not found; use
+ wxLogNull to suppress it).
+
+ @remarks Domains are searched in the last to first order, i.e. catalogs
+ added later override those added before.
*/
- bool IsOk() const;
+ const wxString& GetString(const wxString& origString,
+ const wxString& domain = wxEmptyString) const;
+
+ /**
+ 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).
+
+ Returns original string if translation is not available (in this case an
+ error message is generated the first time a string is not found; use
+ wxLogNull to suppress it).
+
+ This form is used when retrieving translation of string that has different
+ singular and plural form in English or different plural forms in some
+ other language.
+ It takes two extra arguments: @a origString parameter must contain the
+ singular form of the string to be converted.
+
+ It is also used as the key for the search in the catalog.
+ The @a origString2 parameter is the plural form (in English).
+
+ The parameter @a n is used to determine the plural form.
+ If no message catalog is found @a origString is returned if 'n == 1',
+ otherwise @a origString2.
+
+ See GNU gettext manual for additional information on plural forms handling.
+ This method is called by the wxGetTranslation() function and _() macro.
+
+ @remarks Domains are searched in the last to first order, i.e. catalogs
+ added later override those added before.
+ */
+ const wxString& GetString(const wxString& origString,
+ const wxString& origString2,
+ size_t n,
+ const wxString& domain = wxEmptyString) const;
+
+ /**
+ Returns the header value for header @a header.
+ The search for @a header is case sensitive. If an @a domain is passed,
+ this domain is searched. Else all domains will be searched until a
+ header has been found.
+
+ The return value is the value of the header if found. Else this will be empty.
+ */
+ wxString GetHeaderValue(const wxString& header,
+ const wxString& domain = wxEmptyString) const;
};
+/**
+ Abstraction of translations discovery and loading.
+
+ This interface makes it possible to override wxWidgets' default catalogs
+ loading mechanism and load MO files from locations other than the
+ filesystem (e.g. embed them in executable).
+
+ Implementations must implement the LoadCatalog() method.
+
+ @see wxFileTranslationsLoader
+ */
+class wxTranslationsLoader
+{
+public:
+ /// Constructor
+ wxTranslationsLoader() {}
+
+ /**
+ Called to load requested catalog.
+
+ If the catalog is found, LoadCatalog() should call LoadCatalogFile()
+ on @a translations to add the translation.
+
+ @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
+ code, possibly combined with country code or
+ additional modifiers (e.g. "fr", "en_GB" or
+ "ca@valencia").
+
+ @return @true on successful load, @false otherwise
+ */
+ virtual bool LoadCatalog(wxTranslations *translations,
+ const wxString& domain, const wxString& lang) = 0;
+};
+
+/**
+ Standard wxTranslationsLoader implementation.
+
+ This finds catalogs in the filesystem, using the standard Unix layout.
+ This is the default unless you change the loader with
+ wxTranslations::SetLoader().
+
+ Catalogs are searched for in standard places (current directory first, then
+ the system one), but you may also prepend additional directories to the
+ search path with AddCatalogLookupPathPrefix().
+ */
+class wxFileTranslationsLoader : public wxTranslationsLoader
+{
+public:
+ /**
+ Add a prefix to the catalog lookup path: the message catalog files will
+ be looked up under prefix/lang/LC_MESSAGES, prefix/lang and prefix
+ (in this order).
+
+ This only applies to subsequent invocations of
+ wxTranslations::AddCatalog().
+ */
+ static void AddCatalogLookupPathPrefix(const wxString& prefix);
+};
+
// ============================================================================
provided: the _() macro is defined to do the same thing as
wxGetTranslation().
- This function calls wxLocale::GetString().
+ This function calls wxTranslations::GetString().
@note This function is not suitable for literal strings in Unicode builds
since the literal strings must be enclosed into _T() or wxT() macro
wxMsgCatalogFile();
~wxMsgCatalogFile();
- // load the catalog from disk (szDirPrefix corresponds to language)
- bool Load(const wxString& szDirPrefix, const wxString& szName,
+ // load the catalog from disk
+ bool Load(const wxString& filename,
wxPluralFormsCalculatorPtr& rPluralFormsCalculator);
// fills the hash with string-translation pairs
~wxMsgCatalog();
#endif
- // load the catalog from disk (szDirPrefix corresponds to language)
- bool Load(const wxString& dirPrefix, const wxString& name,
+ // load the catalog from disk
+ bool Load(const wxString& filename,
+ const wxString& domain,
const wxString& msgIdCharset);
// get name of the catalog
- wxString GetName() const { return m_name; }
+ wxString GetDomain() const { return m_domain; }
// get the translated string: returns NULL if not found
const wxString *GetString(const wxString& sz, size_t n = size_t(-1)) const;
private:
wxMessagesHash m_messages; // all messages in the catalog
- wxString m_name; // name of the domain
+ wxString m_domain; // name of the domain
#if !wxUSE_UNICODE
// the conversion corresponding to this catalog charset if we installed it
wxPluralFormsCalculatorPtr m_pluralFormsCalculator;
};
-// ----------------------------------------------------------------------------
-// global variables
-// ----------------------------------------------------------------------------
-
-// the list of the directories to search for message catalog files
-static wxArrayString gs_searchPrefixes;
// ============================================================================
// implementation
#endif // __WXMSW__
// ----------------------------------------------------------------------------
-// wxMsgCatalogFile class
+// wxMsgCatalogFile clas
// ----------------------------------------------------------------------------
wxMsgCatalogFile::wxMsgCatalogFile()
{
}
-// return the directories to search for message catalogs under the given
-// prefix, separated by wxPATH_SEP
-static
-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;
-}
-
// open disk file and read in it's contents
-bool wxMsgCatalogFile::Load(const wxString& szDirPrefix, const wxString& szName,
+bool wxMsgCatalogFile::Load(const wxString& filename,
wxPluralFormsCalculatorPtr& rPluralFormsCalculator)
{
- wxCHECK_MSG( szDirPrefix.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 )
- {
- wxString fullname(szDirPrefix);
- fullname << wxS('.') << wxFontMapperBase::GetEncodingName(encSys);
- searchPath << GetFullSearchPath(fullname) << wxPATH_SEP;
- }
-#endif // wxUSE_FONTMAP
-
-
- searchPath += GetFullSearchPath(szDirPrefix);
- if ( szDirPrefix.length() > LEN_LANG && szDirPrefix[LEN_LANG] == wxS('_') )
- {
- // also add 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
- searchPath << wxPATH_SEP
- << GetFullSearchPath(ExtractLang(szDirPrefix));
- }
-
- wxLogTrace(TRACE_I18N, wxS("Looking for \"%s.mo\" in search path \"%s\""),
- szName, searchPath);
-
- wxFileName fn(szName);
- fn.SetExt(wxS("mo"));
-
- wxString strFullName;
- if ( !wxFindFileInPath(&strFullName, searchPath, fn.GetFullPath()) )
- {
- wxLogVerbose(_("catalog file for domain '%s' not found."), szName);
- wxLogTrace(TRACE_I18N, wxS("Catalog \"%s.mo\" not found"), szName);
- return false;
- }
-
- // open file and read its data
- wxLogVerbose(_("using catalog '%s' from '%s'."), szName, strFullName.c_str());
- wxLogTrace(TRACE_I18N, wxS("Using catalog \"%s\"."), strFullName.c_str());
-
- wxFile fileMsg(strFullName);
+ wxFile fileMsg(filename);
if ( !fileMsg.IsOpened() )
return false;
if ( !bValid ) {
// it's either too short or has incorrect magic number
- wxLogWarning(_("'%s' is not a valid message catalog."), strFullName.c_str());
+ wxLogWarning(_("'%s' is not a valid message catalog."), filename.c_str());
return false;
}
}
#endif // !wxUSE_UNICODE
-bool wxMsgCatalog::Load(const wxString& dirPrefix, const wxString& name,
+bool wxMsgCatalog::Load(const wxString& filename,
+ const wxString& domain,
const wxString& msgIdCharset)
{
wxMsgCatalogFile file;
- m_name = name;
+ m_domain = domain;
- if ( !file.Load(dirPrefix, name, m_pluralFormsCalculator) )
+ if ( !file.Load(filename, m_pluralFormsCalculator) )
return false;
if ( !file.FillHash(m_messages, msgIdCharset) )
{
index = m_pluralFormsCalculator->evaluate(n);
}
- wxMessagesHash::const_iterator i;
- if (index != 0)
+ 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 )
{
- i = m_messages.find(wxString(str) + wxChar(index)); // plural
+ wxString fullname(lang);
+ fullname << wxS('.') << wxFontMapperBase::GetEncodingName(encSys);
+ searchPath << GetFullSearchPath(fullname) << wxPATH_SEP;
}
- else
+#endif // wxUSE_FONTMAP
+
+ searchPath += GetFullSearchPath(lang);
+ if ( lang.length() > LEN_LANG && lang[LEN_LANG] == wxS('_') )
{
- i = m_messages.find(str);
+ // also add 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
+ searchPath << wxPATH_SEP
+ << GetFullSearchPath(ExtractLang(lang));
}
- if ( i != m_messages.end() )
+ wxLogTrace(TRACE_I18N, wxS("Looking for \"%s.mo\" in search path \"%s\""),
+ domain, searchPath);
+
+ wxFileName fn(domain);
+ fn.SetExt(wxS("mo"));
+
+ wxString strFullName;
+ if ( !wxFindFileInPath(&strFullName, searchPath, fn.GetFullPath()) )
{
- return &i->second;
+ wxLogVerbose(_("catalog file for domain '%s' not found."), domain);
+ wxLogTrace(TRACE_I18N, wxS("Catalog \"%s.mo\" not found"), domain);
+ return false;
}
- else
- 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);
}
+
// ----------------------------------------------------------------------------
// wxLocale
// ----------------------------------------------------------------------------
m_pszOldLocale = NULL;
m_pOldLocale = wxSetLocale(this);
+ wxTranslations::SetNonOwned(&m_translations);
- m_pMsgCat = NULL;
m_language = wxLANGUAGE_UNKNOWN;
m_initialized = false;
}
#endif
)
{
- wxASSERT_MSG( !m_initialized,
- wxS("you can't call wxLocale::Init more than once") );
-
#if WXWIN_COMPATIBILITY_2_8
wxASSERT_MSG( bConvertEncoding,
wxS("wxLocale::Init with bConvertEncoding=false is no longer supported, add charset to your catalogs") );
#endif
+ bool ret = DoInit(name, shortName, locale);
+
+ // NB: don't use 'lang' here, 'language' may be wxLANGUAGE_DEFAULT
+ m_translations.SetLanguage(shortName);
+
+ if ( bLoadDefault )
+ m_translations.AddStdCatalog();
+
+ return ret;
+}
+
+bool wxLocale::DoInit(const wxString& name,
+ const wxString& shortName,
+ const wxString& locale)
+{
+ wxASSERT_MSG( !m_initialized,
+ wxS("you can't call wxLocale::Init more than once") );
+
m_initialized = true;
m_strLocale = name;
m_strShort = shortName;
}
}
- // load the default catalog with wxWidgets standard messages
- m_pMsgCat = NULL;
- bool bOk = true;
- if ( bLoadDefault )
- {
- bOk = AddCatalog(wxS("wxstd"));
-
- // there may be a catalog with toolkit specific overrides, it is not
- // an error if this does not exist
- if ( bOk )
- {
- wxString port(wxPlatformInfo::Get().GetPortIdName());
- if ( !port.empty() )
- {
- AddCatalog(port.BeforeFirst(wxS('/')).MakeLower());
- }
- }
- }
-
- return bOk;
+ return true;
}
// this language
}
- if ( !Init(name, canonical, retloc,
- (flags & wxLOCALE_LOAD_DEFAULT) != 0) )
+ if ( !DoInit(name, canonical, retloc) )
{
ret = false;
}
if (IsOk()) // setlocale() succeeded
m_language = lang;
- return ret;
-#endif // !WX_NO_LOCALE_SUPPORT
-}
-
+ // NB: don't use 'lang' here, 'language'
+ m_translations.SetLanguage(wx_static_cast(wxLanguage, language));
+ if ( flags & wxLOCALE_LOAD_DEFAULT )
+ m_translations.AddStdCatalog();
-void wxLocale::AddCatalogLookupPathPrefix(const wxString& prefix)
-{
- if ( gs_searchPrefixes.Index(prefix) == wxNOT_FOUND )
- {
- gs_searchPrefixes.Add(prefix);
- }
- //else: already have it
+ return ret;
+#endif // !WX_NO_LOCALE_SUPPORT
}
/*static*/ int wxLocale::GetSystemLanguage()
/* static */
wxString wxLocale::GetLanguageName(int lang)
{
+ if ( lang == wxLANGUAGE_DEFAULT || lang == wxLANGUAGE_UNKNOWN )
+ return wxEmptyString;
+
const wxLanguageInfo *info = GetLanguageInfo(lang);
if ( !info )
return wxEmptyString;
return info->Description;
}
+/* static */
+wxString wxLocale::GetLanguageCanonicalName(int lang)
+{
+ if ( lang == wxLANGUAGE_DEFAULT || lang == wxLANGUAGE_UNKNOWN )
+ return wxEmptyString;
+
+ const wxLanguageInfo *info = GetLanguageInfo(lang);
+ if ( !info )
+ return wxEmptyString;
+ else
+ return info->CanonicalName;
+}
+
/* static */
const wxLanguageInfo *wxLocale::FindLanguageInfo(const wxString& locale)
{
// clean up
wxLocale::~wxLocale()
{
- // free memory
- wxMsgCatalog *pTmpCat;
- while ( m_pMsgCat != NULL ) {
- pTmpCat = m_pMsgCat;
- m_pMsgCat = m_pMsgCat->m_pNext;
- delete pTmpCat;
+ // restore old translations object
+ if ( wxTranslations::Get() == &m_translations )
+ {
+ if ( m_pOldLocale )
+ wxTranslations::SetNonOwned(&m_pOldLocale->m_translations);
+ else
+ wxTranslations::Set(NULL);
}
// restore old locale pointer
free((wxChar *)m_pszOldLocale); // const_cast
}
-// get the translation of given string in current locale
-const wxString& wxLocale::GetString(const wxString& origString,
- const wxString& domain) const
-{
- return GetString(origString, origString, size_t(-1), domain);
-}
-
-const wxString& wxLocale::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,
- wxS("string \"%s\"[%ld] not found in %slocale '%s'."),
- origString, (long)n,
- wxString::Format(wxS("domain '%s' "), domain).c_str(),
- m_strLocale.c_str());
-
- if (n == size_t(-1))
- return GetUntranslatedString(origString);
- else
- return GetUntranslatedString(n == 1 ? origString : origString2);
- }
-
- return *trans;
-}
-
-WX_DECLARE_HASH_SET(wxString, wxStringHash, wxStringEqual,
- wxLocaleUntranslatedStrings);
-
-/* static */
-const wxString& wxLocale::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;
-}
-
-wxString wxLocale::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 *wxLocale::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->GetName() == domain )
- return pMsgCat;
- }
-
- return NULL;
-}
// check if the given locale is provided by OS and C run time
/* static */
return true;
}
-// check if the given catalog is loaded
-bool wxLocale::IsLoaded(const wxString& szDomain) const
-{
- return FindCatalog(szDomain) != NULL;
-}
-
-// add a catalog to our linked list
-bool wxLocale::AddCatalog(const wxString& szDomain)
-{
- return AddCatalog(szDomain, wxLANGUAGE_ENGLISH_US, wxEmptyString);
-}
-
// add a catalog to our linked list
bool wxLocale::AddCatalog(const wxString& szDomain,
wxLanguage msgIdLanguage,
const wxString& msgIdCharset)
-
{
- wxCHECK_MSG( !m_strShort.empty(), false, "must initialize catalog first" );
-
-
- // 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 ( msgIdLanguage == m_language )
- return true;
-
-
- wxMsgCatalog *pMsgCat = new wxMsgCatalog;
-
- if ( pMsgCat->Load(m_strShort, szDomain, msgIdCharset) )
- {
- // 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;
- }
-
- // don't add it because it couldn't be loaded anyway
- delete pMsgCat;
-
-
- // If there's no exact match, we may still get partial match where the
- // (basic) language is same, but the country differs. For example, it's
- // permitted to use en_US strings from sources even if m_language is en_GB:
- const wxLanguageInfo *msgIdLangInfo = GetLanguageInfo(msgIdLanguage);
- if ( msgIdLangInfo &&
- ExtractLang(msgIdLangInfo->CanonicalName) == ExtractLang(m_strShort) )
- {
- return true;
- }
-
- return false;
+#if wxUSE_UNICODE
+ wxUnusedVar(msgIdCharset);
+ return m_translations.AddCatalog(szDomain, msgIdLanguage);
+#else
+ return m_translations.AddCatalog(szDomain, msgIdLanguage, msgIdCharset);
+#endif
}
// ----------------------------------------------------------------------------
DECLARE_DYNAMIC_CLASS(wxLocaleModule)
public:
wxLocaleModule() {}
- bool OnInit() { return true; }
- void OnExit() { wxLocale::DestroyLanguagesDB(); }
+
+ bool OnInit()
+ {
+ return true;
+ }
+
+ void OnExit()
+ {
+ if ( gs_translationsOwned )
+ delete gs_translations;
+ gs_translations = NULL;
+ gs_translationsOwned = true;
+
+ wxLocale::DestroyLanguagesDB();
+ }
};
IMPLEMENT_DYNAMIC_CLASS(wxLocaleModule, wxModule)