// constants
// ----------------------------------------------------------------------------
-// the constants describing the format of ll_CC locale string
-static const size_t LEN_LANG = 2;
-static const size_t LEN_SUBLANG = 2;
-static const size_t LEN_FULL = LEN_LANG + 1 + LEN_SUBLANG; // 1 for '_'
-
#define TRACE_I18N wxS("i18n")
// ============================================================================
namespace
{
-// get just the language part
+// get just the language part ("en" in "en_GB")
inline wxString ExtractLang(const wxString& langFull)
{
- return langFull.Left(LEN_LANG);
+ return langFull.BeforeFirst('_');
}
// helper functions of GetSystemLanguage()
// get everything else (including the leading '_')
inline wxString ExtractNotLang(const wxString& langFull)
{
- return langFull.Mid(LEN_LANG);
+ size_t pos = langFull.find('_');
+ if ( pos != wxString::npos )
+ return langFull.substr(pos);
+ else
+ return wxString();
}
#endif // __UNIX__
// wxLanguageInfo
// ----------------------------------------------------------------------------
-#ifdef __WXMSW__
+#ifdef __WINDOWS__
// helper used by wxLanguageInfo::GetLocaleName() and elsewhere to determine
// whether the locale is Unicode-only (it is if this function returns empty
return locale;
}
-#endif // __WXMSW__
+#endif // __WINDOWS__
// ----------------------------------------------------------------------------
// wxLocale
/*static*/ void wxLocale::DestroyLanguagesDB()
{
- delete ms_languagesDB;
- ms_languagesDB = NULL;
+ wxDELETE(ms_languagesDB);
}
m_pszOldLocale = NULL;
m_pOldLocale = wxSetLocale(this);
- wxTranslations::SetNonOwned(&m_translations);
+
+ // Set translations object, but only if the user didn't do so yet.
+ // This is to preserve compatibility with wx-2.8 where wxLocale was
+ // the only API for translations. wxLocale works as a stack, with
+ // latest-created one being the active one:
+ // wxLocale loc_fr(wxLANGUAGE_FRENCH);
+ // // _() returns French
+ // {
+ // wxLocale loc_cs(wxLANGUAGE_CZECH);
+ // // _() returns Czech
+ // }
+ // // _() returns French again
+ wxTranslations *oldTrans = wxTranslations::Get();
+ if ( !oldTrans ||
+ (m_pOldLocale && oldTrans == &m_pOldLocale->m_translations) )
+ {
+ wxTranslations::SetNonOwned(&m_translations);
+ }
m_language = wxLANGUAGE_UNKNOWN;
m_initialized = false;
const wxString& locale,
bool bLoadDefault
#if WXWIN_COMPATIBILITY_2_8
- ,bool bConvertEncoding
+ ,bool WXUNUSED_UNLESS_DEBUG(bConvertEncoding)
#endif
)
{
bool ret = DoInit(name, shortName, locale);
// NB: don't use 'lang' here, 'language' may be wxLANGUAGE_DEFAULT
- m_translations.SetLanguage(shortName);
+ wxTranslations *t = wxTranslations::Get();
+ if ( t )
+ {
+ t->SetLanguage(shortName);
- if ( bLoadDefault )
- m_translations.AddStdCatalog();
+ if ( bLoadDefault )
+ t->AddStdCatalog();
+ }
return ret;
}
if ( m_pszOldLocale == NULL )
{
- wxLogError(_("locale '%s' can not be set."), szLocale);
+ wxLogError(_("locale '%s' cannot be set."), szLocale);
}
// the short name will be used to look for catalog files as well,
m_language = lang;
// NB: don't use 'lang' here, 'language'
- m_translations.SetLanguage(wx_static_cast(wxLanguage, language));
+ wxTranslations *t = wxTranslations::Get();
+ if ( t )
+ {
+ t->SetLanguage(static_cast<wxLanguage>(language));
- if ( flags & wxLOCALE_LOAD_DEFAULT )
- m_translations.AddStdCatalog();
+ if ( flags & wxLOCALE_LOAD_DEFAULT )
+ t->AddStdCatalog();
+ }
return ret;
#endif // !WX_NO_LOCALE_SUPPORT
langFull.Truncate(posEndLang);
}
- // in addition to the format above, we also can have full language names
- // in LANG env var - for example, SuSE is known to use LANG="german" - so
- // check for this
-
// do we have just the language (or sublang too)?
- bool justLang = langFull.length() == LEN_LANG;
- if ( justLang ||
- (langFull.length() == LEN_FULL && langFull[LEN_LANG] == wxS('_')) )
- {
- // 0. Make sure the lang is according to latest ISO 639
- // (this is necessary because glibc uses iw and in instead
- // of he and id respectively).
-
- // the language itself (second part is the dialect/sublang)
- wxString langOrig = ExtractLang(langFull);
-
- wxString lang;
- if ( langOrig == wxS("iw"))
- lang = wxS("he");
- else if (langOrig == wxS("in"))
- lang = wxS("id");
- else if (langOrig == wxS("ji"))
- lang = wxS("yi");
- else if (langOrig == wxS("no_NO"))
- lang = wxS("nb_NO");
- else if (langOrig == wxS("no_NY"))
- lang = wxS("nn_NO");
- else if (langOrig == wxS("no"))
- lang = wxS("nb_NO");
- else
- lang = langOrig;
+ const bool justLang = langFull.find('_') == wxString::npos;
+
+ // 0. Make sure the lang is according to latest ISO 639
+ // (this is necessary because glibc uses iw and in instead
+ // of he and id respectively).
+
+ // the language itself (second part is the dialect/sublang)
+ wxString langOrig = ExtractLang(langFull);
+
+ wxString lang;
+ if ( langOrig == wxS("iw"))
+ lang = wxS("he");
+ else if (langOrig == wxS("in"))
+ lang = wxS("id");
+ else if (langOrig == wxS("ji"))
+ lang = wxS("yi");
+ else if (langOrig == wxS("no_NO"))
+ lang = wxS("nb_NO");
+ else if (langOrig == wxS("no_NY"))
+ lang = wxS("nn_NO");
+ else if (langOrig == wxS("no"))
+ lang = wxS("nb_NO");
+ else
+ lang = langOrig;
- // did we change it?
- if ( lang != langOrig )
- {
- langFull = lang + ExtractNotLang(langFull);
- }
+ // did we change it?
+ if ( lang != langOrig )
+ {
+ langFull = lang + ExtractNotLang(langFull);
+ }
- // 1. Try to find the language either as is:
- // a) With modifier if set
- if ( !modifier.empty() )
+ // 1. Try to find the language either as is:
+ // a) With modifier if set
+ if ( !modifier.empty() )
+ {
+ wxString langFullWithModifier = langFull + modifier;
+ for ( i = 0; i < count; i++ )
{
- wxString langFullWithModifier = langFull + modifier;
- for ( i = 0; i < count; i++ )
- {
- if ( ms_languagesDB->Item(i).CanonicalName == langFullWithModifier )
- break;
- }
+ if ( ms_languagesDB->Item(i).CanonicalName == langFullWithModifier )
+ break;
}
+ }
- // b) Without modifier
- if ( modifier.empty() || i == count )
+ // b) Without modifier
+ if ( modifier.empty() || i == count )
+ {
+ for ( i = 0; i < count; i++ )
{
- for ( i = 0; i < count; i++ )
- {
- if ( ms_languagesDB->Item(i).CanonicalName == langFull )
- break;
- }
+ if ( ms_languagesDB->Item(i).CanonicalName == langFull )
+ break;
}
+ }
- // 2. If langFull is of the form xx_YY, try to find xx:
- if ( i == count && !justLang )
+ // 2. If langFull is of the form xx_YY, try to find xx:
+ if ( i == count && !justLang )
+ {
+ for ( i = 0; i < count; i++ )
{
- for ( i = 0; i < count; i++ )
+ if ( ms_languagesDB->Item(i).CanonicalName == lang )
{
- if ( ms_languagesDB->Item(i).CanonicalName == lang )
- {
- break;
- }
+ break;
}
}
+ }
- // 3. If langFull is of the form xx, try to find any xx_YY record:
- if ( i == count && justLang )
+ // 3. If langFull is of the form xx, try to find any xx_YY record:
+ if ( i == count && justLang )
+ {
+ for ( i = 0; i < count; i++ )
{
- for ( i = 0; i < count; i++ )
+ if ( ExtractLang(ms_languagesDB->Item(i).CanonicalName)
+ == langFull )
{
- if ( ExtractLang(ms_languagesDB->Item(i).CanonicalName)
- == langFull )
- {
- break;
- }
+ break;
}
}
}
- else // not standard format
+
+
+ if ( i == count )
{
- // try to find the name in verbose description
+ // In addition to the format above, we also can have full language
+ // names in LANG env var - for example, SuSE is known to use
+ // LANG="german" - so check for use of non-standard format and try to
+ // find the name in verbose description.
for ( i = 0; i < count; i++ )
{
if (ms_languagesDB->Item(i).Description.CmpNoCase(langFull) == 0)
UINT codepage = ::GetACP();
encname.Printf(wxS("windows-%u"), codepage);
#elif defined(__WXMAC__)
- // default is just empty string, this resolves to the default system
- // encoding later
+ encname = wxCFStringRef::AsString(
+ CFStringGetNameOfEncoding(CFStringGetSystemEncoding())
+ );
#elif defined(__UNIX_LIKE__)
#if defined(HAVE_LANGINFO_H) && defined(CODESET)
// clean up
wxLocale::~wxLocale()
{
- // restore old translations object
+ // Restore old translations object.
+ // See DoCommonInit() for explanation of why this is needed for backward
+ // compatibility.
if ( wxTranslations::Get() == &m_translations )
{
if ( m_pOldLocale )
wxSetLocale(m_pOldLocale);
wxSetlocale(LC_ALL, m_pszOldLocale);
- free((wxChar *)m_pszOldLocale); // const_cast
+ free(const_cast<char *>(m_pszOldLocale));
}
bool wxLocale::IsAvailable(int lang)
{
const wxLanguageInfo *info = wxLocale::GetLanguageInfo(lang);
- wxCHECK_MSG( info, false, wxS("invalid language") );
+ if ( !info )
+ {
+ // The language is unknown (this normally only happens when we're
+ // passed wxLANGUAGE_DEFAULT), so we can't support it.
+ wxASSERT_MSG( lang == wxLANGUAGE_DEFAULT,
+ wxS("No info for a valid language?") );
+ return false;
+ }
#if defined(__WIN32__)
if ( !info->WinLang )
#elif defined(__UNIX__)
// Test if setting the locale works, then set it back.
- const char *oldLocale = wxSetlocaleTryUTF8(LC_ALL, info->CanonicalName);
- if ( !oldLocale )
- {
- // Some C libraries don't like xx_YY form and require xx only
- oldLocale = wxSetlocaleTryUTF8(LC_ALL, ExtractLang(info->CanonicalName));
- if ( !oldLocale )
- return false;
- }
+ char * const oldLocale = wxStrdupA(setlocale(LC_ALL, NULL));
+
+ // Some platforms don't like xx_YY form and require xx only so test for
+ // it too.
+ const bool
+ available = wxSetlocaleTryUTF8(LC_ALL, info->CanonicalName) ||
+ wxSetlocaleTryUTF8(LC_ALL, ExtractLang(info->CanonicalName));
+
// restore the original locale
wxSetlocale(LC_ALL, oldLocale);
+
+ free(oldLocale);
+
+ if ( !available )
+ return false;
#endif
return true;
}
+
+bool wxLocale::AddCatalog(const wxString& domain)
+{
+ wxTranslations *t = wxTranslations::Get();
+ if ( !t )
+ return false;
+ return t->AddCatalog(domain);
+}
+
+bool wxLocale::AddCatalog(const wxString& domain, wxLanguage msgIdLanguage)
+{
+ wxTranslations *t = wxTranslations::Get();
+ if ( !t )
+ return false;
+ return t->AddCatalog(domain, msgIdLanguage);
+}
+
// add a catalog to our linked list
bool wxLocale::AddCatalog(const wxString& szDomain,
wxLanguage msgIdLanguage,
const wxString& msgIdCharset)
{
+ wxTranslations *t = wxTranslations::Get();
+ if ( !t )
+ return false;
#if wxUSE_UNICODE
wxUnusedVar(msgIdCharset);
- return m_translations.AddCatalog(szDomain, msgIdLanguage);
+ return t->AddCatalog(szDomain, msgIdLanguage);
#else
- return m_translations.AddCatalog(szDomain, msgIdLanguage, msgIdCharset);
+ return t->AddCatalog(szDomain, msgIdLanguage, msgIdCharset);
#endif
}
+bool wxLocale::IsLoaded(const wxString& domain) const
+{
+ wxTranslations *t = wxTranslations::Get();
+ if ( !t )
+ return false;
+ return t->IsLoaded(domain);
+}
+
+wxString wxLocale::GetHeaderValue(const wxString& header,
+ const wxString& domain) const
+{
+ wxTranslations *t = wxTranslations::Get();
+ if ( !t )
+ return wxEmptyString;
+ return t->GetHeaderValue(header, domain);
+}
+
// ----------------------------------------------------------------------------
// accessors for locale-dependent data
// ----------------------------------------------------------------------------
-#if defined(__WXMSW__) || defined(__WXOSX__)
+#if defined(__WINDOWS__) || defined(__WXOSX__)
namespace
{
const char* formatchars =
"dghHmMsSy"
-#ifdef __WXMSW__
+#ifdef __WINDOWS__
"t"
#else
"EawD"
// between 1 and 2 digits for days
fmtWX += "%d";
break;
-#ifdef __WXMSW__
+#ifdef __WINDOWS__
case 3: // ddd
fmtWX += "%a";
break;
wxFAIL_MSG( "too many 'd's" );
}
break;
-#ifndef __WXMSW__
+#ifndef __WINDOWS__
case 'D':
switch ( lastCount )
{
wxASSERT_MSG( lastCount <= 2, "too many 'g's" );
break;
-#ifndef __WXMSW__
+#ifndef __WINDOWS__
case 'a':
fmtWX += "%p";
break;
#endif
-#ifdef __WXMSW__
+#ifdef __WINDOWS__
case 't':
switch ( lastCount )
{
} // anonymous namespace
-#endif // __WXMSW__ || __WXOSX__
+#endif // __WINDOWS__ || __WXOSX__
-#if defined(__WXMSW__)
+#if defined(__WINDOWS__)
namespace
{
} // anonymous namespace
/* static */
-wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory WXUNUSED(cat))
+wxString wxLocale::GetInfo(wxLocaleInfo index, wxLocaleCategory cat)
{
wxUint32 lcid = LOCALE_USER_DEFAULT;
if ( wxGetLocale() )
switch ( index )
{
+ case wxLOCALE_THOUSANDS_SEP:
+ if ( ::GetLocaleInfo(lcid, LOCALE_STHOUSAND, buf, WXSIZEOF(buf)) )
+ str = buf;
+ break;
+
case wxLOCALE_DECIMAL_POINT:
- if ( ::GetLocaleInfo(lcid, LOCALE_SDECIMAL, buf, WXSIZEOF(buf)) )
+ if ( ::GetLocaleInfo(lcid,
+ cat == wxLOCALE_CAT_MONEY
+ ? LOCALE_SMONDECIMALSEP
+ : LOCALE_SDECIMAL,
+ buf,
+ WXSIZEOF(buf)) )
+ {
str = buf;
+
+ // As we get our decimal point separator from Win32 and not the
+ // CRT there is a possibility of mismatch between them and this
+ // can easily happen if the user code called setlocale()
+ // instead of using wxLocale to change the locale. And this can
+ // result in very strange bugs elsewhere in the code as the
+ // assumptions that formatted strings do use the decimal
+ // separator actually fail, so check for it here.
+ wxASSERT_MSG
+ (
+ wxString::Format("%.3f", 1.23).find(str) != wxString::npos,
+ "Decimal separator mismatch -- did you use setlocale()?"
+ "If so, use wxLocale to change the locale instead."
+ );
+ }
break;
case wxLOCALE_SHORT_DATE_FMT:
return str.AsString();
}
-#else // !__WXMSW__ && !__WXOSX__, assume generic POSIX
+#else // !__WINDOWS__ && !__WXOSX__, assume generic POSIX
namespace
{