Allow creating wxUILocale objects for any locale
Creating such objects (without using them for the UI) is supported under all platforms, so allow doing it. Note that this is only supported under Unix systems when locale_t and related functionality is available, but this should be the case just about everywhere by now. Add a test (or, rather, replace an existing test which was disabled by default) checking that we can now get locale information about any locale, not necessarily the currently used one.
This commit is contained in:
@@ -33,9 +33,17 @@ public:
|
|||||||
// It may return NULL in case of failure.
|
// It may return NULL in case of failure.
|
||||||
static wxUILocaleImpl* CreateUserDefault();
|
static wxUILocaleImpl* CreateUserDefault();
|
||||||
|
|
||||||
|
// Create locale object for the given locale.
|
||||||
|
//
|
||||||
|
// It may return NULL in case of failure.
|
||||||
|
static wxUILocaleImpl* CreateForLocale(const wxLocaleIdent& locId);
|
||||||
|
|
||||||
// This function exists only for wxLocale compatibility and creates the
|
// This function exists only for wxLocale compatibility and creates the
|
||||||
// locale corresponding to the given language.
|
// locale corresponding to the given language.
|
||||||
//
|
//
|
||||||
|
// It is implemented in terms of CreateForLocale() for non-MSW platforms,
|
||||||
|
// but under MSW it is different for compatibility reasons.
|
||||||
|
//
|
||||||
// The language passed to this function is a valid language, i.e. neither
|
// The language passed to this function is a valid language, i.e. neither
|
||||||
// wxLANGUAGE_UNKNOWN nor wxLANGUAGE_DEFAULT.
|
// wxLANGUAGE_UNKNOWN nor wxLANGUAGE_DEFAULT.
|
||||||
//
|
//
|
||||||
|
@@ -45,6 +45,9 @@ public:
|
|||||||
// Get the object corresponding to the currently used locale.
|
// Get the object corresponding to the currently used locale.
|
||||||
static const wxUILocale& GetCurrent();
|
static const wxUILocale& GetCurrent();
|
||||||
|
|
||||||
|
// Create the object corresponding to the given locale.
|
||||||
|
explicit wxUILocale(const wxLocaleIdent& localeId);
|
||||||
|
|
||||||
// Get the platform-dependent name of the current locale.
|
// Get the platform-dependent name of the current locale.
|
||||||
wxString GetName() const;
|
wxString GetName() const;
|
||||||
|
|
||||||
@@ -62,7 +65,8 @@ public:
|
|||||||
~wxUILocale();
|
~wxUILocale();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Ctor is private, use static accessor to get objects of this class.
|
// Default ctor is private and exists only for implementation reasons,
|
||||||
|
// wxUILocale objects can't be invalid.
|
||||||
wxUILocale() : m_impl(NULL) { }
|
wxUILocale() : m_impl(NULL) { }
|
||||||
|
|
||||||
// Used by UseDefault().
|
// Used by UseDefault().
|
||||||
|
@@ -94,6 +94,21 @@ public:
|
|||||||
*/
|
*/
|
||||||
static const wxUILocale& GetCurrent();
|
static const wxUILocale& GetCurrent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
Creates the locale corresponding to the given locale identifier.
|
||||||
|
|
||||||
|
In the simplest case, this can be used as following:
|
||||||
|
@code
|
||||||
|
const wxUILocale loc("fr");
|
||||||
|
@endcode
|
||||||
|
but more precise locale identifiers can be used, see wxLocaleIdent
|
||||||
|
description for more details.
|
||||||
|
|
||||||
|
If @a localeId is not recognized or not supported, default ("C") locale
|
||||||
|
is used instead.
|
||||||
|
*/
|
||||||
|
explicit wxUILocale(const wxLocaleIdent& localeId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Compares two strings using comparison rules of the given locale.
|
Compares two strings using comparison rules of the given locale.
|
||||||
|
|
||||||
|
@@ -37,6 +37,31 @@ wxUILocale wxUILocale::ms_current;
|
|||||||
// implementation
|
// implementation
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
#ifndef __WINDOWS__
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
wxUILocaleImpl* wxUILocaleImpl::CreateForLanguage(const wxLanguageInfo& info)
|
||||||
|
{
|
||||||
|
wxLocaleIdent locId;
|
||||||
|
|
||||||
|
// Strings in our language database are of the form "lang[_region[@mod]]".
|
||||||
|
wxString rest;
|
||||||
|
locId.Language(info.CanonicalName.BeforeFirst('_', &rest));
|
||||||
|
|
||||||
|
if ( !rest.empty() )
|
||||||
|
{
|
||||||
|
wxString mod;
|
||||||
|
locId.Region(rest.BeforeFirst('@', &mod));
|
||||||
|
|
||||||
|
if ( !mod.empty() )
|
||||||
|
locId.Modifier(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreateForLocale(locId);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !__WINDOWS__
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
bool wxUILocale::UseDefault()
|
bool wxUILocale::UseDefault()
|
||||||
{
|
{
|
||||||
@@ -84,6 +109,13 @@ const wxUILocale& wxUILocale::GetCurrent()
|
|||||||
return ms_current;
|
return ms_current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxUILocale::wxUILocale(const wxLocaleIdent& localeId)
|
||||||
|
{
|
||||||
|
m_impl = wxUILocaleImpl::CreateForLocale(localeId);
|
||||||
|
if ( !m_impl )
|
||||||
|
m_impl = wxUILocaleImpl::CreateStdC();
|
||||||
|
}
|
||||||
|
|
||||||
void wxUILocale::SetImpl(wxUILocaleImpl* impl)
|
void wxUILocale::SetImpl(wxUILocaleImpl* impl)
|
||||||
{
|
{
|
||||||
delete m_impl;
|
delete m_impl;
|
||||||
|
@@ -142,7 +142,7 @@ wxString wxLocaleIdent::GetName() const
|
|||||||
// LCID-based wxUILocale implementation for MSW
|
// LCID-based wxUILocale implementation for MSW
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
// TODO-XP: Drop it when we don't support XP any longer.
|
// TODO-XP: Replace with wxUILocaleImplName when we don't support XP any longer.
|
||||||
class wxUILocaleImplLCID : public wxUILocaleImpl
|
class wxUILocaleImplLCID : public wxUILocaleImpl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -367,6 +367,21 @@ wxUILocaleImpl* wxUILocaleImpl::CreateForLanguage(const wxLanguageInfo& info)
|
|||||||
return new wxUILocaleImplLCID(info.GetLCID());
|
return new wxUILocaleImplLCID(info.GetLCID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* static */
|
||||||
|
wxUILocaleImpl* wxUILocaleImpl::CreateForLocale(const wxLocaleIdent& locId)
|
||||||
|
{
|
||||||
|
if ( !wxUILocaleImplName::CanUse() )
|
||||||
|
{
|
||||||
|
// We could try finding the LCID matching the name, but support for XP
|
||||||
|
// will be dropped soon, so it just doesn't seem worth to do it (note
|
||||||
|
// that LocaleNameToLCID() itself is not available in XP neither, so we
|
||||||
|
// can't just use it here).
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new wxUILocaleImplName(locId.GetName());
|
||||||
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
int
|
int
|
||||||
wxUILocale::CompareStrings(const wxString& lhs,
|
wxUILocale::CompareStrings(const wxString& lhs,
|
||||||
|
@@ -78,9 +78,10 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static wxUILocaleImplCF* Create(const wxString& name)
|
static wxUILocaleImplCF* Create(const wxLocaleIdent& locId)
|
||||||
{
|
{
|
||||||
CFLocaleRef cfloc = CFLocaleCreate(kCFAllocatorDefault, wxCFStringRef(name));
|
CFLocaleRef cfloc = CFLocaleCreate(kCFAllocatorDefault,
|
||||||
|
wxCFStringRef(locId.GetName()));
|
||||||
if ( !cfloc )
|
if ( !cfloc )
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@@ -125,7 +126,7 @@ wxUILocaleImplCF::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const
|
|||||||
/* static */
|
/* static */
|
||||||
wxUILocaleImpl* wxUILocaleImpl::CreateStdC()
|
wxUILocaleImpl* wxUILocaleImpl::CreateStdC()
|
||||||
{
|
{
|
||||||
return wxUILocaleImplCF::Create("C");
|
return wxUILocaleImplCF::Create(wxLocaleIdent("C"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
@@ -135,9 +136,9 @@ wxUILocaleImpl* wxUILocaleImpl::CreateUserDefault()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
wxUILocaleImpl* wxUILocaleImpl::CreateForLanguage(const wxLanguageInfo& info)
|
wxUILocaleImpl* wxUILocaleImpl::CreateForLocale(const wxLocaleIdent& locId)
|
||||||
{
|
{
|
||||||
return wxUILocaleImplCF::Create(info.CanonicalName);
|
return wxUILocaleImplCF::Create(locId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
|
@@ -41,8 +41,7 @@ namespace
|
|||||||
class wxUILocaleImplUnix : public wxUILocaleImpl
|
class wxUILocaleImplUnix : public wxUILocaleImpl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Locale argument may be NULL to not change it at all.
|
explicit wxUILocaleImplUnix(wxLocaleIdent locId);
|
||||||
explicit wxUILocaleImplUnix(const char* locale);
|
|
||||||
~wxUILocaleImplUnix() wxOVERRIDE;
|
~wxUILocaleImplUnix() wxOVERRIDE;
|
||||||
|
|
||||||
bool Use() wxOVERRIDE;
|
bool Use() wxOVERRIDE;
|
||||||
@@ -50,8 +49,23 @@ public:
|
|||||||
wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxOVERRIDE;
|
wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxOVERRIDE;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// This pointer is owned by this class and may be NULL.
|
#ifdef HAVE_LANGINFO_H
|
||||||
char* const m_name;
|
// Call nl_langinfo_l() if available, or nl_langinfo() otherwise.
|
||||||
|
const char* GetLangInfo(nl_item item) const;
|
||||||
|
#endif // HAVE_LANGINFO_H
|
||||||
|
|
||||||
|
#ifdef HAVE_LOCALE_T
|
||||||
|
// On success, set m_locale and change m_locId to the given one.
|
||||||
|
// Otherwise just return false.
|
||||||
|
bool TryCreateLocale(const wxLocaleIdent& locId);
|
||||||
|
#endif // HAVE_LOCALE_T
|
||||||
|
|
||||||
|
wxLocaleIdent m_locId;
|
||||||
|
|
||||||
|
#ifdef HAVE_LOCALE_T
|
||||||
|
// Initially null, allocated on demand when needed, use GetLocale().
|
||||||
|
locale_t m_locale;
|
||||||
|
#endif // HAVE_LOCALE_T
|
||||||
|
|
||||||
wxDECLARE_NO_COPY_CLASS(wxUILocaleImplUnix);
|
wxDECLARE_NO_COPY_CLASS(wxUILocaleImplUnix);
|
||||||
};
|
};
|
||||||
@@ -146,26 +160,76 @@ const char *wxSetlocaleTryAll(int c, const wxString& lc)
|
|||||||
// wxUILocale implementation for Unix
|
// wxUILocale implementation for Unix
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
wxUILocaleImplUnix::wxUILocaleImplUnix(const char* locale)
|
#ifdef HAVE_LOCALE_T
|
||||||
: m_name(locale ? strdup(locale) : NULL)
|
|
||||||
|
bool
|
||||||
|
wxUILocaleImplUnix::TryCreateLocale(const wxLocaleIdent& locId)
|
||||||
{
|
{
|
||||||
|
m_locale = newlocale(LC_ALL_MASK, locId.GetName(), NULL);
|
||||||
|
if ( !m_locale )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_locId = locId;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAVE_LOCALE_T
|
||||||
|
|
||||||
|
wxUILocaleImplUnix::wxUILocaleImplUnix(wxLocaleIdent locId)
|
||||||
|
: m_locId(locId)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_LOCALE_T
|
||||||
|
if ( !TryCreateLocale(locId) )
|
||||||
|
{
|
||||||
|
// Try to find a variant of this locale available on this system: first
|
||||||
|
// of all, using just the language, without the territory, typically
|
||||||
|
// does _not_ work under Linux, so try adding one if we don't have it.
|
||||||
|
if ( locId.GetRegion().empty() )
|
||||||
|
{
|
||||||
|
const wxLanguageInfo* const info =
|
||||||
|
wxLocale::FindLanguageInfo(locId.GetLanguage());
|
||||||
|
if ( info )
|
||||||
|
{
|
||||||
|
wxString region = info->CanonicalName.AfterFirst('_');
|
||||||
|
if ( !region.empty() )
|
||||||
|
{
|
||||||
|
// We never have encoding in our canonical names, but we
|
||||||
|
// can have modifiers, so get rid of them if necessary.
|
||||||
|
region = region.BeforeFirst('@');
|
||||||
|
|
||||||
|
TryCreateLocale(locId.Region(region));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// And sometimes the locale without encoding is not available, but one
|
||||||
|
// with UTF-8 encoding is, so try this too.
|
||||||
|
if ( !m_locale && locId.GetCharset().empty() )
|
||||||
|
{
|
||||||
|
TryCreateLocale(locId.Charset("UTF-8"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // HAVE_LOCALE_T
|
||||||
}
|
}
|
||||||
|
|
||||||
wxUILocaleImplUnix::~wxUILocaleImplUnix()
|
wxUILocaleImplUnix::~wxUILocaleImplUnix()
|
||||||
{
|
{
|
||||||
free(m_name);
|
#ifdef HAVE_LOCALE_T
|
||||||
|
if ( m_locale )
|
||||||
|
freelocale(m_locale);
|
||||||
|
#endif // HAVE_LOCALE_T
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
wxUILocaleImplUnix::Use()
|
wxUILocaleImplUnix::Use()
|
||||||
{
|
{
|
||||||
if ( !m_name )
|
if ( m_locId.IsDefault() )
|
||||||
{
|
{
|
||||||
// This is the default locale, it is already in use.
|
// This is the default locale, it is already in use.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const wxString& shortName = wxString::FromAscii(m_name);
|
const wxString& shortName = m_locId.GetName();
|
||||||
|
|
||||||
if ( !wxSetlocaleTryAll(LC_ALL, shortName) )
|
if ( !wxSetlocaleTryAll(LC_ALL, shortName) )
|
||||||
{
|
{
|
||||||
@@ -194,9 +258,25 @@ wxUILocaleImplUnix::Use()
|
|||||||
wxString
|
wxString
|
||||||
wxUILocaleImplUnix::GetName() const
|
wxUILocaleImplUnix::GetName() const
|
||||||
{
|
{
|
||||||
return wxString::FromAscii(m_name ? m_name : setlocale(LC_ALL, NULL));
|
return m_locId.GetName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_LANGINFO_H
|
||||||
|
|
||||||
|
const char*
|
||||||
|
wxUILocaleImplUnix::GetLangInfo(nl_item item) const
|
||||||
|
{
|
||||||
|
#ifdef HAVE_LOCALE_T
|
||||||
|
// We assume that we have nl_langinfo_l() if we have locale_t.
|
||||||
|
if ( m_locale )
|
||||||
|
return nl_langinfo_l(item, m_locale);
|
||||||
|
#endif // HAVE_LOCALE_T
|
||||||
|
|
||||||
|
return nl_langinfo(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAVE_LANGINFO_H
|
||||||
|
|
||||||
wxString
|
wxString
|
||||||
wxUILocaleImplUnix::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const
|
wxUILocaleImplUnix::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const
|
||||||
{
|
{
|
||||||
@@ -206,29 +286,29 @@ wxUILocaleImplUnix::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const
|
|||||||
case wxLOCALE_THOUSANDS_SEP:
|
case wxLOCALE_THOUSANDS_SEP:
|
||||||
#ifdef MON_THOUSANDS_SEP
|
#ifdef MON_THOUSANDS_SEP
|
||||||
if ( cat == wxLOCALE_CAT_MONEY )
|
if ( cat == wxLOCALE_CAT_MONEY )
|
||||||
return nl_langinfo(MON_THOUSANDS_SEP);
|
return GetLangInfo(MON_THOUSANDS_SEP);
|
||||||
#endif
|
#endif
|
||||||
return nl_langinfo(THOUSEP);
|
return GetLangInfo(THOUSEP);
|
||||||
|
|
||||||
case wxLOCALE_DECIMAL_POINT:
|
case wxLOCALE_DECIMAL_POINT:
|
||||||
#ifdef MON_DECIMAL_POINT
|
#ifdef MON_DECIMAL_POINT
|
||||||
if ( cat == wxLOCALE_CAT_MONEY )
|
if ( cat == wxLOCALE_CAT_MONEY )
|
||||||
return nl_langinfo(MON_DECIMAL_POINT);
|
return GetLangInfo(MON_DECIMAL_POINT);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return nl_langinfo(RADIXCHAR);
|
return GetLangInfo(RADIXCHAR);
|
||||||
|
|
||||||
case wxLOCALE_SHORT_DATE_FMT:
|
case wxLOCALE_SHORT_DATE_FMT:
|
||||||
return nl_langinfo(D_FMT);
|
return GetLangInfo(D_FMT);
|
||||||
|
|
||||||
case wxLOCALE_DATE_TIME_FMT:
|
case wxLOCALE_DATE_TIME_FMT:
|
||||||
return nl_langinfo(D_T_FMT);
|
return GetLangInfo(D_T_FMT);
|
||||||
|
|
||||||
case wxLOCALE_TIME_FMT:
|
case wxLOCALE_TIME_FMT:
|
||||||
return nl_langinfo(T_FMT);
|
return GetLangInfo(T_FMT);
|
||||||
|
|
||||||
case wxLOCALE_LONG_DATE_FMT:
|
case wxLOCALE_LONG_DATE_FMT:
|
||||||
return wxGetDateFormatOnly(nl_langinfo(D_T_FMT));
|
return wxGetDateFormatOnly(GetLangInfo(D_T_FMT));
|
||||||
|
|
||||||
default:
|
default:
|
||||||
wxFAIL_MSG( "unknown wxLocaleInfo value" );
|
wxFAIL_MSG( "unknown wxLocaleInfo value" );
|
||||||
@@ -249,19 +329,19 @@ wxUILocaleImplUnix::GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const
|
|||||||
/* static */
|
/* static */
|
||||||
wxUILocaleImpl* wxUILocaleImpl::CreateStdC()
|
wxUILocaleImpl* wxUILocaleImpl::CreateStdC()
|
||||||
{
|
{
|
||||||
return new wxUILocaleImplUnix(NULL);
|
return new wxUILocaleImplUnix("C");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
wxUILocaleImpl* wxUILocaleImpl::CreateUserDefault()
|
wxUILocaleImpl* wxUILocaleImpl::CreateUserDefault()
|
||||||
{
|
{
|
||||||
return new wxUILocaleImplUnix("");
|
return new wxUILocaleImplUnix(wxLocaleIdent());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */
|
/* static */
|
||||||
wxUILocaleImpl* wxUILocaleImpl::CreateForLanguage(const wxLanguageInfo& info)
|
wxUILocaleImpl* wxUILocaleImpl::CreateForLocale(const wxLocaleIdent& locId)
|
||||||
{
|
{
|
||||||
return new wxUILocaleImplUnix(info.CanonicalName);
|
return new wxUILocaleImplUnix(locId);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // wxUSE_INTL
|
#endif // wxUSE_INTL
|
||||||
|
@@ -240,17 +240,17 @@ TEST_CASE("wxLocale::Default", "[locale]")
|
|||||||
|
|
||||||
#endif // wxUSE_UNICODE
|
#endif // wxUSE_UNICODE
|
||||||
|
|
||||||
// This test doesn't run by default as it only works in locales using decimal
|
TEST_CASE("wxUILocale::GetInfo", "[uilocale]")
|
||||||
// point as separator, which doesn't need to be the case.
|
|
||||||
TEST_CASE("wxUILocale::GetInfo", "[.][uilocale]")
|
|
||||||
{
|
{
|
||||||
REQUIRE( wxUILocale::UseDefault() );
|
CHECK( wxUILocale("en").GetInfo(wxLOCALE_DECIMAL_POINT) == "." );
|
||||||
|
CHECK( wxUILocale("de").GetInfo(wxLOCALE_DECIMAL_POINT) == "," );
|
||||||
|
CHECK( wxUILocale("ru").GetInfo(wxLOCALE_DECIMAL_POINT) == "," );
|
||||||
|
|
||||||
const wxUILocale& loc = wxUILocale::GetCurrent();
|
// This one shows that "Swiss High German" locale (de_CH) correctly uses
|
||||||
|
// dot, and not comma, as decimal separator, even under macOS, where POSIX
|
||||||
WARN( "Using locale " << loc.GetName() );
|
// APIs use incorrect (identical to "German") definitions for this locale.
|
||||||
|
CHECK( wxUILocale(wxLocaleIdent("de").Region("CH")).
|
||||||
CHECK( loc.GetInfo(wxLOCALE_DECIMAL_POINT) == "." );
|
GetInfo(wxLOCALE_DECIMAL_POINT) == "." );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just a small helper to make the test below shorter.
|
// Just a small helper to make the test below shorter.
|
||||||
|
Reference in New Issue
Block a user