Files
wxWidgets/src/msw/uilocale.cpp
Vadim Zeitlin 2d719165ca Fix wxMSW build with wxUSE_UNICODE==0
Use wxChar in wxUILocaleImplLCID to allow the code to compile and work
correctly even when GetLocaleInfo() is GetLocaleInfoA() and not the W
version as usual.

Closes #22299.
2022-04-12 23:46:36 +02:00

830 lines
26 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/msw/uilocale.cpp
// Purpose: wxUILocale implementation for MSW
// Author: Vadim Zeitlin
// Created: 2021-07-31
// Copyright: (c) 2021 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#if wxUSE_INTL
#include "wx/uilocale.h"
#include "wx/private/uilocale.h"
#include "wx/msw/private/uilocale.h"
#include "wx/scopedarray.h"
#include "wx/dynlib.h"
#ifndef LOCALE_NAME_USER_DEFAULT
#define LOCALE_NAME_USER_DEFAULT NULL
#endif
#ifndef MUI_LANGUAGE_NAME
#define MUI_LANGUAGE_NAME 8
#endif
#ifndef LOCALE_ICONSTRUCTEDLOCALE
#define LOCALE_ICONSTRUCTEDLOCALE 0x0000007d
#endif
#ifndef LOCALE_RETURN_NUMBER
#define LOCALE_RETURN_NUMBER 0x20000000
#endif
#ifndef LOCALE_SENGLISHDISPLAYNAME
#define LOCALE_SENGLISHDISPLAYNAME 0x00000072
#endif
#ifndef LOCALE_SNATIVEDISPLAYNAME
#define LOCALE_SNATIVEDISPLAYNAME 0x00000073
#endif
#ifndef LOCALE_SENGLISHLANGUAGENAME
#define LOCALE_SENGLISHLANGUAGENAME 0x00001001
#endif
#ifndef LOCALE_SNATIVELANGUAGENAME
#define LOCALE_SNATIVELANGUAGENAME 0x00000004
#endif
#ifndef LOCALE_SENGLISHCOUNTRYNAME
#define LOCALE_SENGLISHCOUNTRYNAME 0x00001002
#endif
#ifndef LOCALE_SNATIVECOUNTRYNAME
#define LOCALE_SNATIVECOUNTRYNAME 0x00000008
#endif
#ifndef LOCALE_IREADINGLAYOUT
#define LOCALE_IREADINGLAYOUT 0x00000070
#endif
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// helper functions
// ----------------------------------------------------------------------------
namespace
{
// Trivial wrapper for ::SetThreadUILanguage().
//
// TODO-XP: Drop this when we don't support XP any longer.
static void wxMSWSetThreadUILanguage(LANGID langid)
{
// SetThreadUILanguage() is available on XP, but with unclear
// behavior, so avoid calling it there.
if ( wxGetWinVersion() >= wxWinVersion_Vista )
{
wxLoadedDLL dllKernel32(wxS("kernel32.dll"));
typedef LANGID(WINAPI *SetThreadUILanguage_t)(LANGID);
SetThreadUILanguage_t pfnSetThreadUILanguage = NULL;
wxDL_INIT_FUNC(pfn, SetThreadUILanguage, dllKernel32);
if (pfnSetThreadUILanguage)
pfnSetThreadUILanguage(langid);
}
}
} // anonymous namespace
void wxUseLCID(LCID lcid)
{
::SetThreadLocale(lcid);
wxMSWSetThreadUILanguage(LANGIDFROMLCID(lcid));
}
// ----------------------------------------------------------------------------
// wxLocaleIdent::GetName() implementation for MSW
// ----------------------------------------------------------------------------
wxString wxLocaleIdent::GetName() const
{
// Construct name in right format:
// Windows: <language>-<script>-<REGION>
wxString name;
if ( !m_language.empty() )
{
name << m_language;
if ( !m_script.empty() )
{
name << "-" << m_script;
}
if ( !m_region.empty() )
{
name << "-" << m_region;
}
if (!m_extension.empty())
{
name << "-" << m_extension;
}
if (!m_sortorder.empty())
{
name << "_" << m_sortorder;
}
}
return name;
}
// ----------------------------------------------------------------------------
// Standard C wxUILocale implementation for MSW
// ----------------------------------------------------------------------------
class wxUILocaleImplStdC : public wxUILocaleImpl
{
public:
// Create object corresponding to the given locale, return NULL if not
// supported.
static wxUILocaleImplStdC* Create()
{
return new wxUILocaleImplStdC();
}
~wxUILocaleImplStdC() wxOVERRIDE
{
}
void Use() wxOVERRIDE
{
}
wxString GetName() const wxOVERRIDE
{
return wxString("C");
}
wxLocaleIdent GetLocaleId() const wxOVERRIDE
{
return wxLocaleIdent().Language("C");
}
wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxOVERRIDE
{
return wxGetStdCLocaleInfo(index, cat);
}
wxString GetLocalizedName(wxLocaleName name, wxLocaleForm WXUNUSED(form)) const wxOVERRIDE
{
wxString str;
switch (name)
{
case wxLOCALE_NAME_LOCALE:
case wxLOCALE_NAME_LANGUAGE:
str = wxString("English");
break;
case wxLOCALE_NAME_COUNTRY:
str = wxString();
break;
default:
wxFAIL_MSG("unknown wxLocaleInfo");
}
return str;
}
wxLayoutDirection GetLayoutDirection() const wxOVERRIDE
{
return wxLayout_Default;
}
int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const wxOVERRIDE
{
const int rc = flags & wxCompare_CaseInsensitive ? lhs.CmpNoCase(rhs)
: lhs.Cmp(rhs);
if (rc < 0)
return -1;
if (rc > 0)
return 1;
return 0;
}
private:
// Ctor is private, use Create() instead.
explicit wxUILocaleImplStdC()
{
}
wxDECLARE_NO_COPY_CLASS(wxUILocaleImplStdC);
};
// ----------------------------------------------------------------------------
// LCID-based wxUILocale implementation for MSW
// ----------------------------------------------------------------------------
// TODO-XP: Replace with wxUILocaleImplName when we don't support XP any longer.
class wxUILocaleImplLCID : public wxUILocaleImpl
{
public:
explicit wxUILocaleImplLCID(LCID lcid)
: m_lcid(lcid)
{
}
void Use() wxOVERRIDE
{
wxUseLCID(m_lcid);
}
wxString GetName() const wxOVERRIDE
{
wxString str;
// Try using newer constant available since Vista which produces names
// more similar to the other platforms.
if ( wxGetWinVersion() >= wxWinVersion_Vista )
{
str = DoGetInfo(LOCALE_SNAME);
}
else // TODO-XP: Drop this branch.
{
// This name constant is available under all systems, including
// pre-Vista ones.
str = DoGetInfo(LOCALE_SENGLANGUAGE);
}
return str;
}
wxLocaleIdent GetLocaleId() const wxOVERRIDE
{
return wxLocaleIdent::FromTag(GetName());
}
wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxOVERRIDE
{
return wxGetInfoFromLCID(m_lcid, index, cat);
}
wxString GetLocalizedName(wxLocaleName name, wxLocaleForm form) const wxOVERRIDE
{
wxString str;
switch (name)
{
case wxLOCALE_NAME_LOCALE:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
{
wxString strLang = DoGetInfo(LOCALE_SNATIVELANGNAME);
wxString strCtry = DoGetInfo(LOCALE_SNATIVECTRYNAME);
str << strLang << " (" << strCtry << ")";
}
break;
case wxLOCALE_FORM_ENGLISH:
{
wxString strLang = DoGetInfo(LOCALE_SENGLANGUAGE);
wxString strCtry = DoGetInfo(LOCALE_SENGCOUNTRY);
str << strLang << " (" << strCtry << ")";
}
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
case wxLOCALE_NAME_LANGUAGE:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
str = DoGetInfo(LOCALE_SNATIVELANGNAME);
break;
case wxLOCALE_FORM_ENGLISH:
str = DoGetInfo(LOCALE_SENGLANGUAGE);
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
case wxLOCALE_NAME_COUNTRY:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
str = DoGetInfo(LOCALE_SNATIVECTRYNAME);
break;
case wxLOCALE_FORM_ENGLISH:
str = DoGetInfo(LOCALE_SENGCOUNTRY);
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
}
return str;
}
wxLayoutDirection GetLayoutDirection() const wxOVERRIDE
{
return wxLayout_Default;
}
int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const wxOVERRIDE
{
// Can't be really implemented on the OS versions where this class is
// used.
return flags & wxCompare_CaseInsensitive
? lhs.CmpNoCase(rhs)
: lhs.Cmp(rhs) ;
}
private:
const LCID m_lcid;
wxString DoGetInfo(LCTYPE lctype) const
{
wxChar buf[256];
if (!::GetLocaleInfo(m_lcid, lctype, buf, WXSIZEOF(buf)))
{
wxLogLastError(wxT("GetLocaleInfo"));
return wxString();
}
return buf;
}
wxDECLARE_NO_COPY_CLASS(wxUILocaleImplLCID);
};
// ----------------------------------------------------------------------------
// Name-based wxUILocale implementation for MSW
// ----------------------------------------------------------------------------
class wxUILocaleImplName : public wxUILocaleImpl
{
public:
// TODO-XP: Get rid of this function and all the code branches handling the
// return value of "false" from it and just always use this class.
static bool CanUse()
{
static int s_canUse = -1;
if ( s_canUse == -1 )
{
// One time only initialization.
s_canUse = 0;
wxLoadedDLL dllKernel32(wxS("kernel32.dll"));
wxDL_INIT_FUNC(ms_, GetLocaleInfoEx, dllKernel32);
if ( !ms_GetLocaleInfoEx )
return false;
wxDL_INIT_FUNC(ms_, SetThreadPreferredUILanguages, dllKernel32);
if ( !ms_SetThreadPreferredUILanguages )
return false;
wxDL_INIT_FUNC(ms_, GetUserPreferredUILanguages, dllKernel32);
if (!ms_GetUserPreferredUILanguages)
return false;
wxDL_INIT_FUNC(ms_, CompareStringEx, dllKernel32);
if ( !ms_CompareStringEx )
return false;
s_canUse = 1;
}
return s_canUse == 1;
}
static wxVector<wxString> GetPreferredUILanguages()
{
wxVector<wxString> preferred;
if (CanUse())
{
ULONG numberOfLanguages = 0;
ULONG bufferSize = 0;
if (ms_GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &numberOfLanguages, NULL, &bufferSize))
{
wxScopedArray<WCHAR> languages(bufferSize);
if (ms_GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &numberOfLanguages, languages.get(), &bufferSize))
{
WCHAR* buf = languages.get();
for (unsigned k = 0; k < numberOfLanguages; ++k)
{
const wxString language(buf);
preferred.push_back(language);
buf += language.length() + 1;
}
}
else
{
wxLogLastError(wxT("GetUserPreferredUILanguages"));
}
}
else
{
wxLogLastError(wxT("GetUserPreferredUILanguages"));
}
}
else
{
const wxLanguageInfos& languagesDB = wxGetLanguageInfos();
size_t count = languagesDB.size();
const LANGID langid = ::GetUserDefaultUILanguage();
if (langid != LOCALE_CUSTOM_UI_DEFAULT)
{
size_t ixLanguage = 0;
wxUint32 lang = PRIMARYLANGID(langid);
wxUint32 sublang = SUBLANGID(langid);
for (ixLanguage = 0; ixLanguage < count; ++ixLanguage)
{
if (languagesDB[ixLanguage].WinLang == lang &&
languagesDB[ixLanguage].WinSublang == sublang)
{
break;
}
}
if (ixLanguage < count)
{
preferred.push_back(languagesDB[ixLanguage].CanonicalName);
}
}
}
return preferred;
}
// Create object corresponding to the default user locale.
static wxUILocaleImplName* CreateDefault()
{
return new wxUILocaleImplName(LOCALE_NAME_USER_DEFAULT);
}
// Create object corresponding to the given locale, return NULL if not
// supported.
static wxUILocaleImplName* Create(const wchar_t* name)
{
// Getting the locale name seems to be the simplest way to see if it's
// really supported: unknown locale result in an error here.
if ( !ms_GetLocaleInfoEx(name, LOCALE_SNAME, NULL, 0) )
return NULL;
// Unfortunately under Windows 10 the call above only fails if the given
// locale name is not a valid BCP 47 identifier. For example,
// valid language codes can have 2 or 3 letters:
// - using name "w" fails
// - using name "wx" succeeds
// - using name "wxy" succeeds
// - using name "wxyz" fails
// and so we need another check on these systems (but note that this
// check must not be done under Windows 7 because there plenty of
// actually supported locales are "constructed") by checking
// whether the locale is "constructed" or not: "not constructed"
// means the locale is a predefined locale, "constructed"
// means the locale is not predefined, but has to be constructed.
// For example, "de-US" would be a constructed locale.
if ( wxGetWinVersion() >= wxWinVersion_10 )
{
// Using LOCALE_ICONSTRUCTEDLOCALE to query the locale status is
// discouraged by Microsoft (the constant is not even defined in a
// Windows header file). However, for now constructed locales will
// be rejected here.
int isConstructed = 0;
if (!ms_GetLocaleInfoEx
(
name,
LOCALE_ICONSTRUCTEDLOCALE | LOCALE_RETURN_NUMBER,
(LPWSTR)&isConstructed,
sizeof(int)
) || isConstructed != 0)
return NULL;
}
return new wxUILocaleImplName(name);
}
~wxUILocaleImplName() wxOVERRIDE
{
free(const_cast<wchar_t*>(m_name));
}
void Use() wxOVERRIDE
{
// Construct a double NUL-terminated buffer.
wchar_t buf[256];
if ( m_name )
wxStrlcpy(buf, m_name, WXSIZEOF(buf) - 1);
else
buf[0] = L'\0';
buf[wxWcslen(buf) + 1] = L'\0';
ULONG num = 1;
if ( !ms_SetThreadPreferredUILanguages(MUI_LANGUAGE_NAME, buf, &num) )
wxLogLastError(wxT("SetThreadPreferredUILanguages"));
}
wxString GetName() const wxOVERRIDE
{
return DoGetInfo(LOCALE_SNAME);
}
wxLocaleIdent GetLocaleId() const wxOVERRIDE
{
return wxLocaleIdent::FromTag(GetName());
}
wxString GetInfo(wxLocaleInfo index, wxLocaleCategory cat) const wxOVERRIDE
{
// TODO-XP: This duplicates code from in wxGetInfoFromLCID(), but
// it's only temporary because we will drop all code using LCID soon.
wxString str;
switch ( index )
{
case wxLOCALE_THOUSANDS_SEP:
str = DoGetInfo(LOCALE_STHOUSAND);
break;
case wxLOCALE_DECIMAL_POINT:
str = DoGetInfo(cat == wxLOCALE_CAT_MONEY
? LOCALE_SMONDECIMALSEP
: LOCALE_SDECIMAL);
break;
case wxLOCALE_SHORT_DATE_FMT:
case wxLOCALE_LONG_DATE_FMT:
case wxLOCALE_TIME_FMT:
str = DoGetInfo(wxGetLCTYPEFormatFromLocalInfo(index));
if ( !str.empty() )
str = wxTranslateFromUnicodeFormat(str);
break;
case wxLOCALE_DATE_TIME_FMT:
// there doesn't seem to be any specific setting for this, so just
// combine date and time ones
//
// we use the short date because this is what "%c" uses by default
// ("%#c" uses long date but we have no way to specify the
// alternate representation here)
str << GetInfo(wxLOCALE_SHORT_DATE_FMT, cat)
<< wxS(' ')
<< GetInfo(wxLOCALE_TIME_FMT, cat);
break;
default:
wxFAIL_MSG( "unknown wxLocaleInfo" );
}
return str;
}
wxString GetLocalizedName(wxLocaleName name, wxLocaleForm form) const wxOVERRIDE
{
// TODO-XP: This duplicates code from in wxGetInfoFromLCID(), but
// it's only temporary because we will drop all code using LCID soon.
// LOCALE_SINTLSYMBOL (Currency 3-letter ISO 4217)
static wxWinVersion ver = wxGetWinVersion();
wxString str;
switch (name)
{
case wxLOCALE_NAME_LOCALE:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
if (ver >= wxWinVersion_7)
{
str = DoGetInfo(LOCALE_SNATIVEDISPLAYNAME);
}
else
{
wxString strLang = DoGetInfo(LOCALE_SNATIVELANGNAME);
wxString strCtry = DoGetInfo(LOCALE_SNATIVECTRYNAME);
str << strLang << " (" << strCtry << ")";
}
break;
case wxLOCALE_FORM_ENGLISH:
if (ver >= wxWinVersion_7)
{
str = DoGetInfo(LOCALE_SENGLISHDISPLAYNAME);
}
else
{
wxString strLang = DoGetInfo(LOCALE_SENGLANGUAGE);
wxString strCtry = DoGetInfo(LOCALE_SENGCOUNTRY);
str << strLang << " (" << strCtry << ")";
}
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
case wxLOCALE_NAME_LANGUAGE:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
str = DoGetInfo((ver >= wxWinVersion_7) ? LOCALE_SNATIVELANGUAGENAME : LOCALE_SNATIVELANGNAME);
break;
case wxLOCALE_FORM_ENGLISH:
str = DoGetInfo((ver >= wxWinVersion_7) ? LOCALE_SENGLISHLANGUAGENAME : LOCALE_SENGLANGUAGE);
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
case wxLOCALE_NAME_COUNTRY:
switch (form)
{
case wxLOCALE_FORM_NATIVE:
str = DoGetInfo((ver >= wxWinVersion_7) ? LOCALE_SNATIVECOUNTRYNAME : LOCALE_SNATIVECTRYNAME);
break;
case wxLOCALE_FORM_ENGLISH:
str = DoGetInfo((ver >= wxWinVersion_7) ? LOCALE_SENGLISHCOUNTRYNAME : LOCALE_SENGCOUNTRY);
break;
default:
wxFAIL_MSG("unknown wxLocaleForm");
}
break;
}
return str;
}
wxLayoutDirection GetLayoutDirection() const wxOVERRIDE
{
if (wxGetWinVersion() >= wxWinVersion_7)
{
wxString str = DoGetInfo(LOCALE_IREADINGLAYOUT);
// str contains a number between 0 and 3:
// 0 = LTR, 1 = RTL, 2 = TTB+RTL, 3 = TTB + LTR
// If str equals 1 return RTL, otherwise LTR
return (str.IsSameAs("1") ? wxLayout_RightToLeft : wxLayout_LeftToRight);
}
else
{
return wxLayout_Default;
}
}
int CompareStrings(const wxString& lhs, const wxString& rhs,
int flags) const wxOVERRIDE
{
DWORD dwFlags = 0;
if ( flags & wxCompare_CaseInsensitive )
dwFlags |= NORM_IGNORECASE;
const int ret = ms_CompareStringEx
(
m_name,
dwFlags,
lhs.wc_str(), -1,
rhs.wc_str(), -1,
NULL, // [out] version information -- not needed
wxRESERVED_PARAM,
wxRESERVED_PARAM
);
switch ( ret )
{
case CSTR_LESS_THAN:
return -1;
case CSTR_EQUAL:
return 0;
case CSTR_GREATER_THAN:
return 1;
}
wxFAIL_MSG(wxS("Unreachable"));
return 0;
}
private:
typedef int (WINAPI *GetLocaleInfoEx_t)(LPCWSTR, LCTYPE, LPWSTR, int);
static GetLocaleInfoEx_t ms_GetLocaleInfoEx;
typedef BOOL (WINAPI *SetThreadPreferredUILanguages_t)(DWORD, CONST WCHAR*, ULONG*);
static SetThreadPreferredUILanguages_t ms_SetThreadPreferredUILanguages;
typedef BOOL (WINAPI *GetUserPreferredUILanguages_t)(DWORD, ULONG*, WCHAR*, ULONG*);
static GetUserPreferredUILanguages_t ms_GetUserPreferredUILanguages;
// Note: we currently don't use NLSVERSIONINFO output parameter and so we
// don't bother dealing with the different sizes of this struct under
// different OS versions and define the function type as using "void*" to
// avoid using this parameter accidentally. If we ever really need to use
// it, we'd need to check the OS version/struct size during run-time.
typedef int (WINAPI *CompareStringEx_t)(LPCWSTR, DWORD,
CONST WCHAR*, int,
CONST WCHAR*, int,
void*, // actually LPNLSVERSIONINFO
void*,
LPARAM);
static CompareStringEx_t ms_CompareStringEx;
// Ctor is private, use CreateDefault() or Create() instead.
//
// Note that "name" can be NULL here (LOCALE_NAME_USER_DEFAULT).
explicit wxUILocaleImplName(const wchar_t* name)
: m_name(name ? wxStrdup(name) : NULL)
{
}
wxString DoGetInfo(LCTYPE lctype) const
{
wchar_t buf[256];
if ( !ms_GetLocaleInfoEx(m_name, lctype, buf, WXSIZEOF(buf)) )
{
wxLogLastError(wxT("GetLocaleInfoEx"));
return wxString();
}
return buf;
}
const wchar_t* const m_name;
wxDECLARE_NO_COPY_CLASS(wxUILocaleImplName);
};
wxUILocaleImplName::GetLocaleInfoEx_t wxUILocaleImplName::ms_GetLocaleInfoEx;
wxUILocaleImplName::SetThreadPreferredUILanguages_t wxUILocaleImplName::ms_SetThreadPreferredUILanguages;
wxUILocaleImplName::GetUserPreferredUILanguages_t wxUILocaleImplName::ms_GetUserPreferredUILanguages;
wxUILocaleImplName::CompareStringEx_t wxUILocaleImplName::ms_CompareStringEx;
// ----------------------------------------------------------------------------
// wxUILocaleImpl implementation
// ----------------------------------------------------------------------------
/* static */
wxUILocaleImpl* wxUILocaleImpl::CreateStdC()
{
return wxUILocaleImplStdC::Create();
}
/* static */
wxUILocaleImpl* wxUILocaleImpl::CreateUserDefault()
{
if ( !wxUILocaleImplName::CanUse() )
return new wxUILocaleImplLCID(LOCALE_USER_DEFAULT);
return wxUILocaleImplName::CreateDefault();
}
/* static */
wxUILocaleImpl* wxUILocaleImpl::CreateForLanguage(const wxLanguageInfo& info)
{
if (!wxUILocaleImplName::CanUse())
{
if (info.WinLang == 0)
{
wxLogWarning(wxS("Locale '%s' not supported by OS."), info.Description);
return NULL;
}
return new wxUILocaleImplLCID(info.GetLCID());
}
else
{
return wxUILocaleImplName::Create(info.LocaleTag.wc_str());
}
}
/* 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 wxUILocaleImplName::Create(locId.GetTag(wxLOCALE_TAGTYPE_WINDOWS).wc_str());
}
/* static */
wxVector<wxString> wxUILocaleImpl::GetPreferredUILanguages()
{
return wxUILocaleImplName::GetPreferredUILanguages();
}
#endif // wxUSE_INTL