// Modified by: Michael N. Filippov <michael@idisys.iae.nsk.su>
// (2003/09/30 - PluralForms support)
// Created: 29/01/98
-// RCS-ID: $Id$
// Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// 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);
}
void wxLocale::DoCommonInit()
{
- m_pszOldLocale = NULL;
+ // Store the current locale in order to be able to restore it in the dtor.
+ m_pszOldLocale = wxSetlocale(LC_ALL, NULL);
+ if ( m_pszOldLocale )
+ m_pszOldLocale = wxStrdup(m_pszOldLocale);
+
m_pOldLocale = wxSetLocale(this);
const wxString& locale,
bool bLoadDefault
#if WXWIN_COMPATIBILITY_2_8
- ,bool bConvertEncoding
+ ,bool WXUNUSED_UNLESS_DEBUG(bConvertEncoding)
#endif
)
{
wxS("no locale to set in wxLocale::Init()") );
}
- const char *oldLocale = wxSetlocale(LC_ALL, szLocale);
- if ( oldLocale )
- m_pszOldLocale = wxStrdup(oldLocale);
- else
- m_pszOldLocale = NULL;
-
- if ( m_pszOldLocale == NULL )
+ if ( !wxSetlocale(LC_ALL, szLocale) )
{
- 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,
#endif // !WX_NO_LOCALE_SUPPORT
}
+namespace
+{
+
+#ifndef __WXOSX__
+// Small helper function: get the value of the given environment variable and
+// return true only if the variable was found and has non-empty value.
+inline bool wxGetNonEmptyEnvVar(const wxString& name, wxString* value)
+{
+ return wxGetEnv(name, value) && !value->empty();
+}
+#endif
+
+} // anonymous namespace
+
/*static*/ int wxLocale::GetSystemLanguage()
{
CreateLanguagesDB();
#if defined(__UNIX__)
// first get the string identifying the language from the environment
wxString langFull;
-#ifdef __WXMAC__
+#ifdef __WXOSX__
wxCFRef<CFLocaleRef> userLocaleRef(CFLocaleCopyCurrent());
// because the locale identifier (kCFLocaleIdentifier) is formatted a little bit differently, eg
str.reset(wxCFRetain((CFStringRef)CFLocaleGetValue(userLocaleRef, kCFLocaleCountryCode)));
langFull += str.AsString();
#else
- if (!wxGetEnv(wxS("LC_ALL"), &langFull) &&
- !wxGetEnv(wxS("LC_MESSAGES"), &langFull) &&
- !wxGetEnv(wxS("LANG"), &langFull))
+ if (!wxGetNonEmptyEnvVar(wxS("LC_ALL"), &langFull) &&
+ !wxGetNonEmptyEnvVar(wxS("LC_MESSAGES"), &langFull) &&
+ !wxGetNonEmptyEnvVar(wxS("LANG"), &langFull))
{
// no language specified, treat it as English
return wxLANGUAGE_ENGLISH_US;
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)
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;
// 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() )
+ const wxLanguageInfo * const
+ info = wxGetLocale() ? GetLanguageInfo(wxGetLocale()->GetLanguage())
+ : NULL;
+ if ( !info )
{
- const wxLanguageInfo * const
- info = GetLanguageInfo(wxGetLocale()->GetLanguage());
- if ( info )
- lcid = info->GetLCID();
+ // wxSetLocale() hadn't been called yet of failed, hence CRT must be
+ // using "C" locale -- but check it to detect bugs that would happen if
+ // this were not the case.
+ wxASSERT_MSG( strcmp(setlocale(LC_ALL, NULL), "C") == 0,
+ wxS("You probably called setlocale() directly instead ")
+ wxS("of using wxLocale and now there is a ")
+ wxS("mismatch between C/C++ and Windows locale.\n")
+ wxS("Things are going to break, please only change ")
+ wxS("locale by creating wxLocale objects to avoid this!") );
+
+
+ // Return the hard coded values for C locale. This is really the right
+ // thing to do as there is no LCID we can use in the code below in this
+ // case, even LOCALE_INVARIANT is not quite the same as C locale (the
+ // only difference is that it uses %Y instead of %y in the date format
+ // but this difference is significant enough).
+ switch ( index )
+ {
+ case wxLOCALE_THOUSANDS_SEP:
+ return wxString();
+
+ case wxLOCALE_DECIMAL_POINT:
+ return ".";
+
+ case wxLOCALE_SHORT_DATE_FMT:
+ return "%m/%d/%y";
+
+ case wxLOCALE_LONG_DATE_FMT:
+ return "%A, %B %d, %Y";
+
+ case wxLOCALE_TIME_FMT:
+ return "%H:%M:%S";
+
+ case wxLOCALE_DATE_TIME_FMT:
+ return "%m/%d/%y %H:%M:%S";
+
+ default:
+ wxFAIL_MSG( "unknown wxLocaleInfo" );
+ }
}
+ const wxUint32 lcid = info->GetLCID();
+
wxString str;
wxChar buf[256];
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
{