Merge branch 'ui-locale'
Add wxUILocale class providing functionality which can be implemented portably for all major platforms, including macOS, and doesn't force the change of the global C locale, unlike wxLocale. See https://github.com/wxWidgets/wxWidgets/pull/2464
This commit is contained in:
@@ -28,11 +28,17 @@
|
||||
#include "wx/wx.h"
|
||||
#endif
|
||||
|
||||
#include "wx/calctrl.h"
|
||||
#include "wx/intl.h"
|
||||
#include "wx/file.h"
|
||||
#include "wx/grid.h"
|
||||
#include "wx/log.h"
|
||||
#include "wx/cmdline.h"
|
||||
#include "wx/numformatter.h"
|
||||
#include "wx/platinfo.h"
|
||||
#include "wx/spinctrl.h"
|
||||
#include "wx/translation.h"
|
||||
#include "wx/uilocale.h"
|
||||
|
||||
#ifndef wxHAS_IMAGES_IN_RESOURCES
|
||||
#include "../sample.xpm"
|
||||
@@ -53,22 +59,29 @@
|
||||
class MyApp: public wxApp
|
||||
{
|
||||
public:
|
||||
MyApp() { m_lang = wxLANGUAGE_UNKNOWN; }
|
||||
MyApp() { m_setLocale = Locale_Ask; }
|
||||
|
||||
virtual void OnInitCmdLine(wxCmdLineParser& parser) wxOVERRIDE;
|
||||
virtual bool OnCmdLineParsed(wxCmdLineParser& parser) wxOVERRIDE;
|
||||
virtual bool OnInit() wxOVERRIDE;
|
||||
|
||||
protected:
|
||||
wxLanguage m_lang; // language specified by user
|
||||
wxLocale m_locale; // locale we'll be using
|
||||
// Specifies whether we should use the current locale or not. By default we
|
||||
// ask the user about it, but it's possible to override this using the
|
||||
// command line options.
|
||||
enum
|
||||
{
|
||||
Locale_Ask,
|
||||
Locale_Set,
|
||||
Locale_Skip
|
||||
} m_setLocale;
|
||||
};
|
||||
|
||||
// Define a new frame type
|
||||
class MyFrame: public wxFrame
|
||||
{
|
||||
public:
|
||||
MyFrame(wxLocale& m_locale);
|
||||
MyFrame();
|
||||
|
||||
public:
|
||||
void OnTestLocaleAvail(wxCommandEvent& event);
|
||||
@@ -80,14 +93,13 @@ public:
|
||||
|
||||
void OnPlay(wxCommandEvent& event);
|
||||
void OnOpen(wxCommandEvent& event);
|
||||
void OnSave(wxCommandEvent& event);
|
||||
void OnTest1(wxCommandEvent& event);
|
||||
void OnTest2(wxCommandEvent& event);
|
||||
void OnTest3(wxCommandEvent& event);
|
||||
void OnTestMsgBox(wxCommandEvent& event);
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
|
||||
wxLocale& m_locale;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -114,59 +126,6 @@ enum
|
||||
INTERNAT_MACRO_9
|
||||
};
|
||||
|
||||
// language data
|
||||
static const wxLanguage langIds[] =
|
||||
{
|
||||
wxLANGUAGE_DEFAULT,
|
||||
wxLANGUAGE_FRENCH,
|
||||
wxLANGUAGE_ITALIAN,
|
||||
wxLANGUAGE_GERMAN,
|
||||
wxLANGUAGE_RUSSIAN,
|
||||
wxLANGUAGE_BULGARIAN,
|
||||
wxLANGUAGE_CZECH,
|
||||
wxLANGUAGE_POLISH,
|
||||
wxLANGUAGE_SWEDISH,
|
||||
#if wxUSE_UNICODE || defined(__WXMOTIF__)
|
||||
wxLANGUAGE_JAPANESE,
|
||||
#endif
|
||||
#if wxUSE_UNICODE
|
||||
wxLANGUAGE_GEORGIAN,
|
||||
wxLANGUAGE_ENGLISH,
|
||||
wxLANGUAGE_ENGLISH_US,
|
||||
wxLANGUAGE_ARABIC,
|
||||
wxLANGUAGE_ARABIC_EGYPT
|
||||
#endif
|
||||
};
|
||||
|
||||
// note that it makes no sense to translate these strings, they are
|
||||
// shown before we set the locale anyhow
|
||||
const wxString langNames[] =
|
||||
{
|
||||
"System default",
|
||||
"French",
|
||||
"Italian",
|
||||
"German",
|
||||
"Russian",
|
||||
"Bulgarian",
|
||||
"Czech",
|
||||
"Polish",
|
||||
"Swedish",
|
||||
#if wxUSE_UNICODE || defined(__WXMOTIF__)
|
||||
"Japanese",
|
||||
#endif
|
||||
#if wxUSE_UNICODE
|
||||
"Georgian",
|
||||
"English",
|
||||
"English (U.S.)",
|
||||
"Arabic",
|
||||
"Arabic (Egypt)"
|
||||
#endif
|
||||
};
|
||||
|
||||
// the arrays must be in sync
|
||||
wxCOMPILE_TIME_ASSERT( WXSIZEOF(langNames) == WXSIZEOF(langIds),
|
||||
LangArraysMismatch );
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// wxWidgets macros
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -181,6 +140,7 @@ wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
|
||||
|
||||
EVT_MENU(INTERNAT_PLAY, MyFrame::OnPlay)
|
||||
EVT_MENU(wxID_OPEN, MyFrame::OnOpen)
|
||||
EVT_MENU(wxID_SAVE, MyFrame::OnSave)
|
||||
EVT_MENU(INTERNAT_TEST_1, MyFrame::OnTest1)
|
||||
EVT_MENU(INTERNAT_TEST_2, MyFrame::OnTest2)
|
||||
EVT_MENU(INTERNAT_TEST_3, MyFrame::OnTest3)
|
||||
@@ -198,11 +158,16 @@ wxIMPLEMENT_APP(MyApp);
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// command line arguments handling
|
||||
|
||||
static const char* OPTION_NO_LOCALE = "no-locale";
|
||||
static const char* OPTION_SET_LOCALE = "set-locale";
|
||||
|
||||
void MyApp::OnInitCmdLine(wxCmdLineParser& parser)
|
||||
{
|
||||
parser.AddParam(_("locale"),
|
||||
wxCMD_LINE_VAL_STRING,
|
||||
wxCMD_LINE_PARAM_OPTIONAL);
|
||||
parser.AddSwitch("n", OPTION_NO_LOCALE,
|
||||
_("skip setting locale on startup"));
|
||||
parser.AddSwitch("y", OPTION_SET_LOCALE,
|
||||
_("do set locale on startup without asking"));
|
||||
|
||||
wxApp::OnInitCmdLine(parser);
|
||||
}
|
||||
@@ -212,17 +177,20 @@ bool MyApp::OnCmdLineParsed(wxCmdLineParser& parser)
|
||||
if ( !wxApp::OnCmdLineParsed(parser) )
|
||||
return false;
|
||||
|
||||
if ( parser.GetParamCount() )
|
||||
if ( parser.Found(OPTION_NO_LOCALE) )
|
||||
{
|
||||
const wxString loc = parser.GetParam();
|
||||
const wxLanguageInfo * const lang = wxLocale::FindLanguageInfo(loc);
|
||||
if ( !lang )
|
||||
m_setLocale = Locale_Skip;
|
||||
}
|
||||
|
||||
if ( parser.Found(OPTION_SET_LOCALE) )
|
||||
{
|
||||
if ( m_setLocale == Locale_Skip )
|
||||
{
|
||||
wxLogError(_("Locale \"%s\" is unknown."), loc);
|
||||
return false;
|
||||
wxLogWarning("--%s option overrides --%s",
|
||||
OPTION_SET_LOCALE, OPTION_NO_LOCALE);
|
||||
}
|
||||
|
||||
m_lang = static_cast<wxLanguage>(lang->Language);
|
||||
m_setLocale = Locale_Set;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -234,57 +202,75 @@ bool MyApp::OnInit()
|
||||
if ( !wxApp::OnInit() )
|
||||
return false;
|
||||
|
||||
if ( m_lang == wxLANGUAGE_UNKNOWN )
|
||||
// For demonstration purposes only, ask the user if they want to run the
|
||||
// program using the current system language. In real programs, we would do
|
||||
// it unconditionally for localized programs -- or never do it at all for
|
||||
// the other ones.
|
||||
const wxLanguageInfo* const
|
||||
langInfo = wxLocale::GetLanguageInfo(wxLANGUAGE_DEFAULT);
|
||||
const wxString
|
||||
langDesc = langInfo ? langInfo->Description
|
||||
: "the default system locale";
|
||||
|
||||
if ( m_setLocale == Locale_Ask )
|
||||
{
|
||||
int lng = wxGetSingleChoiceIndex
|
||||
(
|
||||
_("Please choose language:"),
|
||||
_("Language"),
|
||||
WXSIZEOF(langNames),
|
||||
langNames
|
||||
);
|
||||
m_lang = lng == -1 ? wxLANGUAGE_DEFAULT : langIds[lng];
|
||||
m_setLocale = wxMessageBox
|
||||
(
|
||||
wxString::Format
|
||||
(
|
||||
"Would you like to use the program in %s?",
|
||||
langDesc
|
||||
),
|
||||
"wxWidgets i18n (internat) sample",
|
||||
wxYES_NO
|
||||
) == wxYES ? Locale_Set : Locale_Skip;
|
||||
}
|
||||
|
||||
// don't use wxLOCALE_LOAD_DEFAULT flag so that Init() doesn't return
|
||||
// false just because it failed to load wxstd catalog
|
||||
if ( !m_locale.Init(m_lang, wxLOCALE_DONT_LOAD_DEFAULT) )
|
||||
if ( m_setLocale == Locale_Set )
|
||||
{
|
||||
wxLogWarning(_("This language is not supported by the system."));
|
||||
if ( !wxUILocale::UseDefault() )
|
||||
{
|
||||
wxLogWarning("Failed to initialize the default system locale.");
|
||||
}
|
||||
|
||||
// continue nevertheless
|
||||
}
|
||||
|
||||
// normally this wouldn't be necessary as the catalog files would be found
|
||||
// in the default locations, but when the program is not installed the
|
||||
// catalogs are in the build directory where we wouldn't find them by
|
||||
// default
|
||||
wxLocale::AddCatalogLookupPathPrefix(".");
|
||||
// Independently of whether we succeeded to set the locale or not, try
|
||||
// to load the translations (for the default system language) here.
|
||||
|
||||
// Initialize the catalogs we'll be using
|
||||
const wxLanguageInfo* pInfo = wxLocale::GetLanguageInfo(m_lang);
|
||||
if (!m_locale.AddCatalog("internat"))
|
||||
{
|
||||
wxLogError(_("Couldn't find/load the 'internat' catalog for locale '%s'."),
|
||||
pInfo ? pInfo->GetLocaleName() : _("unknown"));
|
||||
}
|
||||
// normally this wouldn't be necessary as the catalog files would be found
|
||||
// in the default locations, but when the program is not installed the
|
||||
// catalogs are in the build directory where we wouldn't find them by
|
||||
// default
|
||||
wxFileTranslationsLoader::AddCatalogLookupPathPrefix(".");
|
||||
|
||||
// Now try to add wxstd.mo so that loading "NOTEXIST.ING" file will produce
|
||||
// a localized error message:
|
||||
m_locale.AddCatalog("wxstd");
|
||||
// NOTE: it's not an error if we couldn't find it!
|
||||
// Create the object for message translation and set it up for global use.
|
||||
wxTranslations* const trans = new wxTranslations();
|
||||
wxTranslations::Set(trans);
|
||||
|
||||
// this catalog is installed in standard location on Linux systems and
|
||||
// shows that you may make use of the standard message catalogs as well
|
||||
//
|
||||
// if it's not installed on your system, it is just silently ignored
|
||||
// Initialize the catalogs we'll be using.
|
||||
if ( !trans->AddCatalog("internat") )
|
||||
{
|
||||
wxLogError(_("Couldn't find/load 'internat' catalog for %s."),
|
||||
langDesc);
|
||||
}
|
||||
|
||||
// Now try to add wxstd.mo so that loading "NOTEXIST.ING" file will produce
|
||||
// a localized error message:
|
||||
trans->AddCatalog("wxstd");
|
||||
// NOTE: it's not an error if we couldn't find it!
|
||||
|
||||
// this catalog is installed in standard location on Linux systems and
|
||||
// shows that you may make use of the standard message catalogs as well
|
||||
//
|
||||
// if it's not installed on your system, it is just silently ignored
|
||||
#ifdef USE_COREUTILS_MO
|
||||
wxLocale::AddCatalogLookupPathPrefix("/usr/share/locale");
|
||||
g_loadedCoreutilsMO = m_locale.AddCatalog("coreutils");
|
||||
wxFileTranslationsLoader::AddCatalogLookupPathPrefix("/usr/share/locale");
|
||||
g_loadedCoreutilsMO = trans->AddCatalog("coreutils");
|
||||
#endif // USE_COREUTILS_MO
|
||||
}
|
||||
|
||||
// Create the main frame window
|
||||
MyFrame *frame = new MyFrame(m_locale);
|
||||
MyFrame *frame = new MyFrame();
|
||||
|
||||
// Show the frame
|
||||
frame->Show(true);
|
||||
@@ -297,11 +283,10 @@ bool MyApp::OnInit()
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// main frame constructor
|
||||
MyFrame::MyFrame(wxLocale& locale)
|
||||
MyFrame::MyFrame()
|
||||
: wxFrame(NULL,
|
||||
wxID_ANY,
|
||||
_("International wxWidgets App")),
|
||||
m_locale(locale)
|
||||
_("International wxWidgets App"))
|
||||
{
|
||||
SetIcon(wxICON(sample));
|
||||
|
||||
@@ -316,6 +301,7 @@ MyFrame::MyFrame(wxLocale& locale)
|
||||
|
||||
wxMenu *test_menu = new wxMenu;
|
||||
test_menu->Append(wxID_OPEN, _("&Open bogus file"), _("Shows a wxWidgets localized error message"));
|
||||
test_menu->Append(wxID_SAVE, _("&Save dummy file"), _("Shows a localized standard dialog"));
|
||||
test_menu->Append(INTERNAT_PLAY, _("&Play a game"), _("A little game; hint: 17 is a lucky number for many"));
|
||||
test_menu->AppendSeparator();
|
||||
test_menu->Append(INTERNAT_TEST_1, _("&1 _() (gettext)"), _("Tests the _() macro"));
|
||||
@@ -360,13 +346,79 @@ MyFrame::MyFrame(wxLocale& locale)
|
||||
// this demonstrates RTL support in wxStatusBar:
|
||||
CreateStatusBar(1);
|
||||
|
||||
// this demonstrates RTL layout mirroring for Arabic locales
|
||||
wxPanel* const panel = new wxPanel(this);
|
||||
|
||||
wxSizer* const topSizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// create controls showing the locale being used
|
||||
topSizer->Add(new wxStaticText
|
||||
(
|
||||
panel,
|
||||
wxID_ANY,
|
||||
wxString::Format
|
||||
(
|
||||
_("Current UI locale: %s; C locale: %s"),
|
||||
wxUILocale::GetCurrent().GetName(),
|
||||
setlocale(LC_ALL, NULL)
|
||||
)
|
||||
),
|
||||
wxSizerFlags().Center().Border());
|
||||
|
||||
// create some controls affected by the locale
|
||||
|
||||
// this demonstrates RTL layout mirroring for Arabic locales and using
|
||||
// locale-specific decimal separator in wxSpinCtrlDouble.
|
||||
wxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(new wxStaticText(this, wxID_ANY, _("First")),
|
||||
wxSizerFlags().Border());
|
||||
sizer->Add(new wxStaticText(this, wxID_ANY, _("Second")),
|
||||
wxSizerFlags().Border());
|
||||
SetSizer(sizer);
|
||||
sizer->Add(new wxStaticText(panel, wxID_ANY, _("Numeric input:")),
|
||||
wxSizerFlags().Center().Border());
|
||||
|
||||
wxSpinCtrlDouble* const spin = new wxSpinCtrlDouble(panel, wxID_ANY);
|
||||
spin->SetDigits(2);
|
||||
spin->SetValue(12.34);
|
||||
sizer->Add(spin, wxSizerFlags().Center().Border());
|
||||
|
||||
topSizer->Add(sizer, wxSizerFlags().Center());
|
||||
|
||||
// show that week days and months names are translated too
|
||||
topSizer->Add(new wxCalendarCtrl(panel, wxID_ANY),
|
||||
wxSizerFlags().Center().Border());
|
||||
|
||||
// another control using locale-specific number and date format
|
||||
wxGrid* const grid = new wxGrid(panel, wxID_ANY,
|
||||
wxDefaultPosition, wxDefaultSize,
|
||||
wxBORDER_SIMPLE);
|
||||
grid->CreateGrid(2, 2);
|
||||
grid->HideRowLabels();
|
||||
|
||||
grid->SetColLabelValue(0, _("Number"));
|
||||
grid->SetColFormatFloat(0);
|
||||
grid->SetCellValue(0, 0, wxNumberFormatter::ToString(3.14159265, -1));
|
||||
|
||||
grid->SetColLabelValue(1, _("Date"));
|
||||
grid->SetColFormatDate(1);
|
||||
grid->SetCellValue(0, 1, "Today");
|
||||
|
||||
topSizer->Add(grid, wxSizerFlags().Center().Border());
|
||||
|
||||
// show the difference (in decimal and thousand separator, hence use a
|
||||
// floating point number > 1000) between wxString::Format() and
|
||||
// wxNumberFormatter: the former uses the current C locale, while the
|
||||
// latter uses the UI locale
|
||||
topSizer->Add(new wxStaticText
|
||||
(
|
||||
panel,
|
||||
wxID_ANY,
|
||||
wxString::Format
|
||||
(
|
||||
_("Number in UI locale: %s; in C locale: %.2f"),
|
||||
wxNumberFormatter::ToString(1234567.89, 2),
|
||||
1234567.89
|
||||
)
|
||||
),
|
||||
wxSizerFlags().Center().Border());
|
||||
|
||||
panel->SetSizer(topSizer);
|
||||
topSizer->SetSizeHints(this);
|
||||
}
|
||||
|
||||
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
|
||||
@@ -376,20 +428,10 @@ void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
|
||||
|
||||
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
wxString localeInfo;
|
||||
wxString locale = m_locale.GetLocale();
|
||||
wxString sysname = m_locale.GetSysName();
|
||||
wxString canname = m_locale.GetCanonicalName();
|
||||
|
||||
localeInfo.Printf(_("Language: %s\nSystem locale name: %s\nCanonical locale name: %s\n"),
|
||||
locale, sysname, canname );
|
||||
|
||||
wxMessageDialog dlg(
|
||||
this,
|
||||
wxString(_("I18n sample\n(c) 1998, 1999 Vadim Zeitlin and Julian Smart"))
|
||||
+ "\n\n"
|
||||
+ localeInfo,
|
||||
_("About Internat"),
|
||||
_("I18n sample\n(c) 1998, 1999 Vadim Zeitlin and Julian Smart"),
|
||||
_("About Internat"),
|
||||
wxOK | wxICON_INFORMATION
|
||||
);
|
||||
dlg.ShowModal();
|
||||
@@ -510,6 +552,13 @@ void MyFrame::OnOpen(wxCommandEvent& WXUNUSED(event))
|
||||
wxFile file("NOTEXIST.ING");
|
||||
}
|
||||
|
||||
void MyFrame::OnSave(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
// show this file dialog just to check that the locale-specific elements in
|
||||
// it (such as dates) follow the current locale convnetions
|
||||
wxSaveFileSelector(_("Dummy file dialog"), ".ext", "dummy", this);
|
||||
}
|
||||
|
||||
void MyFrame::OnTest1(wxCommandEvent& WXUNUSED(event))
|
||||
{
|
||||
const wxString& title = _("Testing _() (gettext)");
|
||||
|
Reference in New Issue
Block a user