wxArtProvider and Translation Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
MortenMacFly
Experienced Solver
Experienced Solver
Posts: 73
Joined: Thu Feb 09, 2006 8:13 am

wxArtProvider and Translation

Post by MortenMacFly »

Settings: Windows 10, 64 bit, wxWidgets 3.1.5 (monolitic, shared DLL), GCC 8.1.0
I have a strange error when I enable translations in my application. This line of code:

Code: Select all

tbiInsertCopy = tbrToolBar->AddTool(idToolBarItemInsertCopy, _("Insert copy"), wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(_T("wxART_COPY")),wxART_TOOLBAR), wxNullBitmap, wxITEM_NORMAL, _("Insert a copy of the selected item"), wxEmptyString);
...as part of the initialisation of the toolbar of the mainframe throws an exception:

Code: Select all

[Main Instruction]
A debugging check in this application has failed.
[Content]
../../src/common/intl.cpp(1643): assert "wxString::Format("%.3f", 1.23).find(str) != wxString::npos" failed in GetInfoFromLCID(): Decimal separator mismatch -- did you use setlocale()?If so, use wxLocale to change the locale instead.
Note that "Decimal separator mismatch -- did you use setlocale()?If so, use wxLocale to change the locale instead." is not the case for me.

Beforehand, other icons are added the same way to the tool bar, just fine. Here is the back-trace:

Code: Select all

(anonymous namespace)::GetInfoFromLCID
wxLocale::GetInfo
wxString::FromCDouble
(anonymous namespace)::wxPNGImageData::DoLoadPNGFile
wxPNGHandler::LoadFile
wxImage::DoLoad
wxImage::LoadFile
(anonymous namespace)::wxTangoArtProvider::CreateBitmap
wxArtProvider::GetBitmap
MainFrame::MainFrame
MainApp::OnInit
...am I using wxArtProvider in a wrong way? Maybe there is some experience like that from other users - I thought in case I do something weird I start asking here first...
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7459
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: wxArtProvider and Translation

Post by ONEEYEMAN »

Hi,
Are you calling setlocale() or not? Why do you say "it is not an issue for me"?

Can we see some actual code?

Thank you.
MortenMacFly
Experienced Solver
Experienced Solver
Posts: 73
Joined: Thu Feb 09, 2006 8:13 am

Re: wxArtProvider and Translation

Post by MortenMacFly »

Sorry for the delay, I somehow did not get notified that there is an answer...
ONEEYEMAN wrote: Tue May 04, 2021 4:25 pm Are you calling setlocale() or not? Why do you say "it is not an issue for me"?
No, I don't call setlocale() - that's why I meant that is not the issue.
ONEEYEMAN wrote: Tue May 04, 2021 4:25 pm Can we see some actual code?
I posted the code in question, maybe I should add that the variable "tbiInsertCopy" is a "wxToolBarToolBase*"

What other code do you want to see? I attach the initialisation here, maybe (?):

Code: Select all

  // Check, if language info is available for this system...
  wxLocale::AddCatalogLookupPathPrefix(locale_path);
  const wxLanguageInfo* info = wxLocale::GetLanguageInfo(wxLANGUAGE_DEFAULT);
  if (!info)
    return; // Unable t retrieve locale information from system

  // Query wxLocale is locale is available...
  if ( !wxLocale::IsAvailable(info->Language) )
    return; // For this language there is no translation, so stick to default

  // Initialise wxLocale, but don't use wxLOCALE_LOAD_DEFAULT flag so that
  // Init() doesn't return false just because it failed to load wxstd catalog.
  mLocale.reset( new wxLocale() );
  if ( !mLocale->Init(info->Language, wxLOCALE_DONT_LOAD_DEFAULT) )
  {
    mLocale.reset();
    return; // This language is no supported by the system, so stick to default
  }

  // Now append the sub-path for the system locale to locate the MO file(s)...
  locale_path.Alloc(locale_path.Len() + 16);
  locale_path.Append(wxT('/'));
  locale_path.Append(info->CanonicalName);

  // Try to open that sub-path to enumerate MO file(s)...
  if ( !wxDir::Exists(locale_path) )
  {
    mLocale.reset();
    return; // There is no sub-path (e.g. de_DE) with locale file for the language
  }

  // Now add all catalogues available in that sub-path...
  wxDir    mo_dir(locale_path);
  wxString mo_file;
  if (   !mo_dir.IsOpened()
      || !mo_dir.GetFirst(&mo_file, wxT("*.mo"), wxDIR_FILES) )
  {
    mLocale.reset();
    return; // Not a single MO file in the path?! -> Missing installation?!
  }
  else
  {
    // Add all catalogues available accordingly...
    do
    { mLocale->AddCatalog(mo_file); }
    while ( mo_dir.GetNext(&mo_file) );
  }
User avatar
evstevemd
Part Of The Furniture
Part Of The Furniture
Posts: 2409
Joined: Wed Jan 28, 2009 11:57 am
Location: United Republic of Tanzania

Re: wxArtProvider and Translation

Post by evstevemd »

I guess locale mismatch somewhere. In some locale 1.23 is interpreted as<one><thousands separator><two><three> while in others it is intepreted as <one><decimal point><two><three>. So my guess is, your current locale translates it as thousands separator and somehow is confused, since there is no thousands there, it may be expected a comma instead of a dot.

Make sure that correct locale is loaded. Since you didn't provide any information of what is actually happening with the last pot's code, it is hard to guess. But perhaps the note below in Init should give you idea
The call of this function has several global side effects which you should understand: first of all, the application locale is changed - note that this will affect many of standard C library functions such as printf() or strftime(). Second, this wxLocale object becomes the new current global locale for the application and so all subsequent calls to wxGetTranslation() will try to translate the messages using the message catalogs for this locale.
Chief Justice: We have trouble dear citizens!
Citizens: What it is his honor?
Chief Justice:Our president is an atheist, who will he swear to?
MortenMacFly
Experienced Solver
Experienced Solver
Posts: 73
Joined: Thu Feb 09, 2006 8:13 am

Re: wxArtProvider and Translation

Post by MortenMacFly »

evstevemd wrote: Fri May 07, 2021 10:21 am I guess locale mismatch somewhere. In some locale 1.23 is interpreted as<one><thousands separator><two><three> while in others it is intepreted as <one><decimal point><two><three>. So my guess is, your current locale translates it as thousands separator and somehow is confused, since there is no thousands there, it may be expected a comma instead of a dot.
I also guess someting like this is happening. However, its wxArt - so there is no string whatsoever but an icon for a toolbar that gets loaded and this confuses me. Please note, that the application and translation runs just fine after this error message. So I doubt initalisation or alike is that much wrong.
evstevemd wrote: Fri May 07, 2021 10:21 am Make sure that correct locale is loaded. Since you didn't provide any information of what is actually happening with the last pot's code, it is hard to guess.
Currently, in that folder is only one locale file which is de_DE. And this is correctly loaded. So the locale initialisation method I posted above just runs fine and returns with success.

Just this annoying message is there... :-(

That's why I am asking if anyone also using wxArtProvider has similar experience.
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: wxArtProvider and Translation

Post by PB »

MortenMacFly wrote: Sun May 09, 2021 4:43 am However, its wxArt - so there is no string whatsoever but an icon for a toolbar that gets loaded and this confuses me.
AFAICT, the assert message you posted does not indicate this comes from the wxArtProvider. I thought it came from using _() in the AddTool() for the label and help string. However, the back trace points to wxString::FromCDouble() used for wxImage::SetOption() in wxPNGImageData::DoLoadPNGFile()?

What is the decimal separator in your app returned by wxLocale::GetLocaleInfo(wxLOCALE_DECIMAL_POINT)?

Where/when do you init the locale and which class is mlocale a member variable of? (its name implies local scope but I guess it is just a different naming convention)?
Last edited by PB on Sun May 09, 2021 8:37 am, edited 1 time in total.
MortenMacFly
Experienced Solver
Experienced Solver
Posts: 73
Joined: Thu Feb 09, 2006 8:13 am

Re: wxArtProvider and Translation

Post by MortenMacFly »

PB wrote: Sun May 09, 2021 7:44 am AFAICT, the assert message you posted does not indicate this comes from the wxArtProvider. I thought it came from using _() in the AddTool() for the label and help string.
That might be, but those two strings have no number in it.
PB wrote: Sun May 09, 2021 7:44 am However, the back trace points to wxString::FromCDouble() used for wxImage::SetOption() in wxPNGImageData::DoLoadPNGFile()?
It seems so. I've compiled a debug version of wxWidgets now and attached the full back-trace below. I am still confused. That doesn't seem to make sense to me. I am also no using PNG's... does wxArtProvider use PNG's internally?
PB wrote: Sun May 09, 2021 7:44 am What is the decimal separator in your app returned by wxLocale::GetLocaleInfo(wxLOCALE_DECIMAL_POINT)?
Its German, so its a colon (,).
PB wrote: Sun May 09, 2021 7:44 am Where do you init the locale and which class is mlocale a member variable of?
Initialisation takes place in the App (derived from wxApp), overiding the OnInit() method, just as I do and see it in other projects, too.

Here is the back-trace. If I just could get a clue why on earth it fails when loading a standard wx image resource?!

Code: Select all

-------------------
Error occurred on Sunday, May 9, 2021 at 10:21:01.

Tool.exe.exe caused a Breakpoint at location 00503592 in module Tool.exe.exe.

Registers:
eax=00000001 ebx=00000000 ecx=08550000 edx=08550000 esi=085e0d08 edi=085a2704
eip=00503592 esp=0825bc60 ebp=0825bf48 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202

AddrPC   Params
00503592 0825BFC0 00000407 00000001  Tool.exe.exe!(anonymous namespace)::GetInfoFromLCID  [D:/Devel/wxWidgets31/build/msw/../../src/common/intl.cpp @ 1638]
  1636:                 // assumptions that formatted strings do use the decimal
  1637:                 // separator actually fail, so check for it here.
> 1638:                 wxASSERT_MSG
  1639:                 (
  1640:                     wxString::Format("%.3f", 1.23).find(str) != wxString::npos,
005039AA 0825BFC0 00000001 00000000  Tool.exe.exe!wxLocale::GetInfo  [D:/Devel/wxWidgets31/build/msw/../../src/common/intl.cpp @ 1737]
  1735:     }
  1736:
> 1737:     return GetInfoFromLCID(info->GetLCID(), index, cat);
  1738: }
  1739:
0051C121 0825C1D8 9999999A 403C5999  Tool.exe.exe!wxString::FromCDouble  [D:/Devel/wxWidgets31/build/msw/../../src/common/string.cpp @ 1858]
  1856: #if wxUSE_INTL
  1857:     wxString sep = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT,
> 1858:                                      wxLOCALE_CAT_NUMBER);
  1859: #else // !wxUSE_INTL
  1860:     // As above, this is the most common alternative value. Notice that here it
0064FBCB 0825C9C8 0825C2E8 00000000  Tool.exe.exe!(anonymous namespace)::wxPNGImageData::DoLoadPNGFile  [D:/Devel/wxWidgets31/build/msw/../../src/common/imagpng.cpp @ 402]
   400:                 */
   401:                 image->SetOption(wxIMAGE_OPTION_RESOLUTIONX,
>  402:                     wxString::FromCDouble((double) resX / 100.0, 2));
   403:                 image->SetOption(wxIMAGE_OPTION_RESOLUTIONY,
   404:                     wxString::FromCDouble((double) resY / 100.0, 2));
0064FEAA 0825C9C8 0825C9A4 00000001  Tool.exe.exe!wxPNGHandler::LoadFile  [D:/Devel/wxWidgets31/build/msw/../../src/common/imagpng.cpp @ 431]
   429:
   430:     wxPNGImageData data;
>  431:     data.DoLoadPNGFile(image, wxinfo);
   432:
   433:     if ( !data.ok )
006448C2 08582818 0825C9A4 FFFFFFFF  Tool.exe.exe!wxImage::DoLoad  [D:/Devel/wxWidgets31/build/msw/../../src/common/image.cpp @ 2866]
  2864:         posOld = stream.TellI();
  2865:
> 2866:     if ( !handler.LoadFile(this, stream,
  2867:                            (M_IMGDATA->m_loadFlags & Load_Verbose) != 0, index) )
  2868:     {
00645394 0825C9A4 0000000F FFFFFFFF  Tool.exe.exe!wxImage::LoadFile  [D:/Devel/wxWidgets31/build/msw/../../src/common/image.cpp @ 2974]
  2972:     }
  2973:
> 2974:     return DoLoad(*handler, stream, index);
  2975: }
  2976:
00B468DC 0825C9A4 0000000F FFFFFFFF  Tool.exe.exe!wxImage::wxImage  [D:/Devel/wxWidgets31/build/msw/../../include/wx/image.h @ 318]
   316: #if wxUSE_STREAMS
   317:     wxImage( wxInputStream& stream, wxBitmapType type = wxBITMAP_TYPE_ANY, int index = -1 )
>  318:         { LoadFile( stream, type, index ); }
   319:     wxImage( wxInputStream& stream, const wxString& mimetype, int index = -1 )
   320:         { LoadFile( stream, mimetype, index ); }
00605CCC 0858F8E8 0825F058 0825F038  Tool.exe.exe!(anonymous namespace)::wxTangoArtProvider::CreateBitmap  [D:/Devel/wxWidgets31/build/msw/../../src/common/arttango.cpp @ 298]
   296:         }
   297:
>  298:         wxImage image(is, wxBITMAP_TYPE_PNG);
   299:         if ( !image.IsOk() )
   300:         {
00603CE6 0825F030 0825F058 0825F038  Tool.exe.exe!wxArtProvider::GetBitmap  [D:/Devel/wxWidgets31/build/msw/../../src/common/artprov.cpp @ 255]
   253:              node; node = node->GetNext())
   254:         {
>  255:             bmp = node->GetData()->CreateBitmap(id, client, size);
   256:             if ( bmp.IsOk() )
   257:                 break;
004A3E41 0855E234 00000001 0855E258  Tool.exe.exe!ToolFrame::ToolFrame  [D:/Devel/Code/Tool/ToolMain.cpp @ 367]
   365:   tbiEdit = tbrToolBar->AddTool(idToolBarItemEdit, _("Edit"), wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(_T("wxART_EXECUTABLE_FILE")),wxART_TOOLBAR), wxNullBitmap, wxITEM_NORMAL, _("Edit selected item"), wxEmptyString);
   366:   tbiRemove = tbrToolBar->AddTool(idToolBarItemRemove, _("Remove"), wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(_T("wxART_DEL_BOOKMARK")),wxART_TOOLBAR), wxNullBitmap, wxITEM_NORMAL, _("Remove selected item"), wxEmptyString);
>  367:   tbiInsertCopy = tbrToolBar->AddTool(idToolBarItemInsertCopy, _("Insert copy"), wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(_T("wxART_COPY")),wxART_TOOLBAR), wxNullBitmap, wxITEM_NORMAL, _("Insert a copy of the selected item"), wxEmptyString);
   368:   tbrToolBar->AddSeparator();
   369:   tbiInfo = tbrToolBar->AddTool(idToolBarItemInfo, _("Info"), wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(_T("wxART_INFORMATION")),wxART_TOOLBAR), wxNullBitmap, wxITEM_NORMAL, _("Show info about selected item"), wxEmptyString);
0049D5F5 00000000 0825FE48 75D5D8BD  Tool.exe.exe!ToolApp::OnInit  [D:/Devel/Code/Tool/ToolApp.cpp @ 83]
    81:       InitLocale(); // Internationalisation initialisation / setup
    82:
>   83:     mToolFrame = new ToolFrame(mCmdLine, mTranslation, mLogFile, mConfigFile);
    84:     return (nullptr!=mToolFrame);
    85:   }
00B094CE 01318150 0857B8D8 00000003  Tool.exe.exe!wxAppConsoleBase::CallOnInit  [D:/Devel/wxWidgets31/include/wx/app.h @ 93]
    91:
    92:     // This gives wxCocoa a chance to call OnInit() with a memory pool in place
>   93:     virtual bool CallOnInit() { return OnInit(); }
    94:
    95:     // Called before OnRun(), this is a good place to do initialization -- if
006BBF02 01318150 0857B8D8 0825FE60  Tool.exe.exe!wxEntryReal  [D:/Devel/wxWidgets31/build/msw/../../src/common/init.cpp @ 488]
   486:     {
   487:         // app initialization
>  488:         if ( !wxTheApp->CallOnInit() )
   489:         {
   490:             // don't call OnExit() if OnInit() failed
0055A488 01318150 0857B8D8 0825FF68  Tool.exe.exe!wxEntry  [D:/Devel/wxWidgets31/build/msw/../../src/msw/main.cpp @ 184]
   182: int wxEntry(int& argc, wxChar **argv)
   183: {
>  184:     return wxEntryReal(argc, argv);
   185: }
   186:
0055A52D 00400000 00000000 085561D6  Tool.exe.exe!wxEntry  [D:/Devel/wxWidgets31/build/msw/../../src/msw/main.cpp @ 296]
   294:         return -1;
   295:
>  296:     return wxEntry(wxArgs.argc, wxArgs.argv);
   297: }
   298:
0049D358 00400000 00000000 085561D6  Tool.exe.exe!WinMain@16  [D:/Devel/Code/Tool/ToolApp.cpp @ 32]
    30: #include <wx/utils.h>    // wxGetUserId
    31:
>   32: wxIMPLEMENT_APP(ToolApp);
    33:
    34: bool ToolApp::OnInit()
00C37CBD 0856FFB0 00000003 08560798  Tool.exe.exe!main
0040138B 00000000 7770FA29 0036E000  Tool.exe.exe!__tmainCRTStartup
7770FA29 0036E000 0C2F834A 00000000  KERNEL32.DLL!@BaseThreadInitThunk@12
77A07A4E FFFFFFFF 77A28901 00000000  ntdll.dll!__RtlUserThreadStart
77A07A1E 00401480 0036E000 00000000  ntdll.dll!__RtlUserThreadStart@8

Tool.exe.exe	2.1.0.5
ntdll.dll   	10.0.19041.928
KERNEL32.DLL	10.0.19041.928
KERNELBASE.dll	10.0.19041.906
ADVAPI32.dll	10.0.19041.610
msvcrt.dll  	7.0.19041.546
sechost.dll 	10.0.19041.906
RPCRT4.dll  	10.0.19041.928
COMDLG32.DLL	10.0.19041.906
combase.dll 	10.0.19041.928
ucrtbase.dll	10.0.19041.789
COMCTL32.DLL	6.10.19041.844
shcore.dll  	10.0.19041.746
GDI32.dll   	10.0.19041.746
USER32.dll  	10.0.19041.906
win32u.dll  	10.0.19041.906
gdi32full.dll	10.0.19041.928
SHLWAPI.dll 	10.0.19041.746
msvcp_win.dll	10.0.19041.789
SHELL32.dll 	10.0.19041.906
CRYPT32.dll 	10.0.19041.844
ole32.dll   	10.0.19041.746
OLEAUT32.dll	10.0.19041.804
wldap32.dll 	10.0.19041.546
WS2_32.dll  	10.0.19041.546
UxTheme.dll 	10.0.19041.746
VERSION.dll 	10.0.19041.546
OLEACC.dll  	7.2.19041.746
WINMM.DLL   	10.0.19041.546
exchndl.dll 	0.9.3.0
WINSPOOL.DRV	10.0.19041.746
PSAPI.DLL   	10.0.19041.546
mgwhelp.dll 	0.9.3.0
dbghelp.dll 	10.0.19041.685
dbgcore.DLL 	10.0.19041.685
IMM32.DLL   	10.0.19041.546
kernel.appcore.dll	10.0.19041.546
bcryptPrimitives.dll	10.0.19041.662
SspiCli.dll 	10.0.19041.906
MSCTF.dll   	10.0.19041.906
TextShaping.dll
windows.storage.dll	10.0.19041.906
Wldp.dll    	10.0.19041.662
WindowsCodecs.dll	10.0.19041.928
bcrypt.dll  	10.0.19041.546
profapi.dll 	10.0.19041.844
DUser.dll   	10.0.19041.546
atlthunk.dll	10.0.19041.546
textinputframework.dll	10.0.19041.906
CoreUIComponents.dll	10.0.19041.546
CoreMessaging.dll	10.0.19041.867
ntmarta.dll 	10.0.19041.546
wintypes.dll	10.0.19041.928
symsrv.dll  	10.0.19041.685
wininet.dll 	11.0.19041.844
iertutil.dll	11.0.19041.906
ondemandconnroutehelper.dll	10.0.19041.546
winhttp.dll 	10.0.19041.906
mswsock.dll 	10.0.19041.546
IPHLPAPI.DLL	10.0.19041.546
NSI.dll     	10.0.19041.610
WINNSI.DLL  	10.0.19041.546
urlmon.dll  	11.0.19041.906
DNSAPI.dll  	10.0.19041.928
rasadhlp.dll	10.0.19041.546
fwpuclnt.dll	10.0.19041.546

Windows 10.0.19042
DrMingw 0.9.3
User avatar
doublemax
Moderator
Moderator
Posts: 19116
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxArtProvider and Translation

Post by doublemax »

Assert messages usually don't lie, I'm pretty sure the problem lies within your wxLocale initialization. Did you trace through the init code you posted a while ago and does it actually run to the end? I assume mLocale is a smart pointer? Just for a test, try using a normal pointer, or just make the wxLocale itself a member variable.
Use the source, Luke!
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: wxArtProvider and Translation

Post by PB »

There must be more going on. My locale (Czech) also uses comma instead of period as the decimal separator. However, this code runs as expected

Code: Select all

#include <wx/wx.h>
#include <wx/artprov.h>
#include <wx/intl.h>

class MyApp : public wxApp
{
    wxLocale m_locale;
public:
    bool OnInit() override
    {
        m_locale.Init();

        wxChar threadDecimalSeparator[8]{0};
        wxChar userDefaultDecimalSeparator[8]{0};

        ::GetLocaleInfo(::GetThreadLocale(), LOCALE_SDECIMAL, threadDecimalSeparator, WXSIZEOF(threadDecimalSeparator));
        ::GetLocaleInfo(::GetUserDefaultLCID(), LOCALE_SDECIMAL, userDefaultDecimalSeparator, WXSIZEOF(userDefaultDecimalSeparator));


        wxFrame* frame = new wxFrame(nullptr, wxID_ANY, "Toolbar test");
        wxToolBar* toolBar = new wxToolBar(frame, wxID_ANY);

        toolBar->AddTool(wxID_ANY, _("Insert copy"), wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(_T("wxART_COPY")),wxART_TOOLBAR), wxNullBitmap, wxITEM_NORMAL, _("Insert a copy of the selected item"), wxEmptyString);
        toolBar->Realize();
        frame->SetToolBar(toolBar);
        frame->Show();

        wxLogMessage("Decimal separator for: \n  wxLocale = '%s'\n  Thread = '%s'\n  User default = '%s'",
            wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER), threadDecimalSeparator, userDefaultDecimalSeparator);

        return true;
    }
}; wxIMPLEMENT_APP(MyApp);
If you remove your locale-related code, the assert disappears, right? I briefly looked at the code but could not find anything wrong it... Does your thread/user default locale decimal separator matches wxLocale one (see the code above)?

As for why PNG. wxART_COPY comes from wxArtTangoProvider, which stores the bitmap as in-memory PNG (see file art\tango\edit_copy.h).
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: wxArtProvider and Translation

Post by PB »

EDITED to remove the incorrect information
I could not reproduce using your code with this

Code: Select all

#include <wx/wx.h>
#include <wx/artprov.h>
#include <wx/dir.h>
#include <wx/intl.h>

class MyApp : public wxApp
{
    std::shared_ptr<wxLocale> mLocale;

    void MyInitLocale()
    {
    wxString locale_path;

// Check, if language info is available for this system...
  wxLocale::AddCatalogLookupPathPrefix(locale_path);
  const wxLanguageInfo* info = wxLocale::GetLanguageInfo(wxLANGUAGE_DEFAULT);
  if (!info)
    return; // Unable t retrieve locale information from system

  // Query wxLocale is locale is available...
  if ( !wxLocale::IsAvailable(info->Language) )
    return; // For this language there is no translation, so stick to default

  // Initialise wxLocale, but don't use wxLOCALE_LOAD_DEFAULT flag so that
  // Init() doesn't return false just because it failed to load wxstd catalog.
  mLocale.reset( new wxLocale() );
  if ( !mLocale->Init(info->Language, wxLOCALE_DONT_LOAD_DEFAULT) )
  {
    mLocale.reset();
    return; // This language is no supported by the system, so stick to default
  }

  // Now append the sub-path for the system locale to locate the MO file(s)...
  locale_path.Alloc(locale_path.Len() + 16);
  locale_path.Append(wxT('/'));
  locale_path.Append(info->CanonicalName);

  // Try to open that sub-path to enumerate MO file(s)...
  if ( !wxDir::Exists(locale_path) )
  {
    mLocale.reset();
    return; // There is no sub-path (e.g. de_DE) with locale file for the language
  }

  // Now add all catalogues available in that sub-path...
  wxDir    mo_dir(locale_path);
  wxString mo_file;
  if (   !mo_dir.IsOpened()
      || !mo_dir.GetFirst(&mo_file, wxT("*.mo"), wxDIR_FILES) )
  {
    mLocale.reset();
    return; // Not a single MO file in the path?! -> Missing installation?!
  }
  else
  {
    // Add all catalogues available accordingly...
    do
    { mLocale->AddCatalog(mo_file); }
    while ( mo_dir.GetNext(&mo_file) );
  }

    }
public:
    bool OnInit() override
    {
        MyInitLocale();

        wxChar threadDecimalSeparator[8]{0};
        wxChar userDefaultDecimalSeparator[8]{0};

        ::GetLocaleInfo(::GetThreadLocale(), LOCALE_SDECIMAL, threadDecimalSeparator, WXSIZEOF(threadDecimalSeparator));
        ::GetLocaleInfo(::GetUserDefaultLCID(), LOCALE_SDECIMAL, userDefaultDecimalSeparator, WXSIZEOF(userDefaultDecimalSeparator));


        wxFrame* frame = new wxFrame(nullptr, wxID_ANY, "Toolbar test");
        wxToolBar* toolBar = new wxToolBar(frame, wxID_ANY);

        toolBar->AddTool(wxID_ANY, _("Insert copy"), wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(_T("wxART_COPY")),wxART_TOOLBAR), wxNullBitmap, wxITEM_NORMAL, _("Insert a copy of the selected item"), wxEmptyString);
        toolBar->Realize();
        frame->SetToolBar(toolBar);
        frame->Show();

        wxLogMessage("Decimal separator for: \n  wxLocale = '%s'\n  Thread = '%s'\n  User default = '%s'",
            wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER), threadDecimalSeparator, userDefaultDecimalSeparator);

        return true;
    }
}; wxIMPLEMENT_APP(MyApp);
locale-decsep.png
locale-decsep.png (5.47 KiB) Viewed 3159 times
I obviously do not have the catalogs but I do not see how loading the translations could affect this.
MortenMacFly
Experienced Solver
Experienced Solver
Posts: 73
Joined: Thu Feb 09, 2006 8:13 am

Re: wxArtProvider and Translation

Post by MortenMacFly »

PB wrote: Sun May 09, 2021 10:08 am I could not reproduce using your code with this
This kind of drives me nuts.
PB wrote: Sun May 09, 2021 9:16 am If you remove your locale-related code, the assert disappears, right?
Yes.
PB wrote: Sun May 09, 2021 9:16 am Does your thread/user default locale decimal separator matches wxLocale one (see the code above)?
I tried this piece of code at the end n the InitLocale method:

Code: Select all

  wxString sep = wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER);
  wxMessageBox(wxString::Format(_("Decimal number is: '%f'. Decimal point is: '%s'."), 1.23, sep.wx_str()));;
And guess what: It _is_ a wrong decimal point. It should be colon (,) but it is dot (.).
doublemax wrote: Sun May 09, 2021 9:08 am Did you trace through the init code you posted a while ago and does it actually run to the end?
Yes I did, and it finishes correctly w/o error.
doublemax wrote: Sun May 09, 2021 9:08 am I assume mLocale is a smart pointer?
It is a std::unique_ptr<wxLocale> and it is already a member variable of the derives App class.
doublemax wrote: Sun May 09, 2021 9:08 am Just for a test, try using a normal pointer, or just make the wxLocale itself a member variable.
This has no effect, unfortunately. I also unsuccessfully tried to remove the wxLOCALE_DONT_LOAD_DEFAULT flag.

I have one more suspect: I am linking statically against libCURL. But I checked the calls / signatures of libCURL and all its dependencies I don't see that it calls setlocale. Also, I could not reproduce the issue on the wx ArtProvider sample code (from wx sources). I "enhanced" it so be initialised the same way as my app and did some string translations. So the issue must lie in my sources... just where?

But I can tell the following: The issues dos not appear if you locale (of your local computer) has the dot (.) as decimal point.

I'm afraid I have to figure out my self by bisecting further. Bu I am very thankful for you help!
MortenMacFly
Experienced Solver
Experienced Solver
Posts: 73
Joined: Thu Feb 09, 2006 8:13 am

Re: wxArtProvider and Translation

Post by MortenMacFly »

PB wrote: Sun May 09, 2021 9:16 am

Code: Select all

#include <wx/wx.h>
#include <wx/artprov.h>
#include <wx/intl.h>

class MyApp : public wxApp
{
    wxLocale m_locale;
public:
    bool OnInit() override
    {
        m_locale.Init();

        wxChar threadDecimalSeparator[8]{0};
        wxChar userDefaultDecimalSeparator[8]{0};

        ::GetLocaleInfo(::GetThreadLocale(), LOCALE_SDECIMAL, threadDecimalSeparator, WXSIZEOF(threadDecimalSeparator));
        ::GetLocaleInfo(::GetUserDefaultLCID(), LOCALE_SDECIMAL, userDefaultDecimalSeparator, WXSIZEOF(userDefaultDecimalSeparator));


        wxFrame* frame = new wxFrame(nullptr, wxID_ANY, "Toolbar test");
        wxToolBar* toolBar = new wxToolBar(frame, wxID_ANY);

        toolBar->AddTool(wxID_ANY, _("Insert copy"), wxArtProvider::GetBitmap(wxART_MAKE_ART_ID_FROM_STR(_T("wxART_COPY")),wxART_TOOLBAR), wxNullBitmap, wxITEM_NORMAL, _("Insert a copy of the selected item"), wxEmptyString);
        toolBar->Realize();
        frame->SetToolBar(toolBar);
        frame->Show();

        wxLogMessage("Decimal separator for: \n  wxLocale = '%s'\n  Thread = '%s'\n  User default = '%s'",
            wxLocale::GetInfo(wxLOCALE_DECIMAL_POINT, wxLOCALE_CAT_NUMBER), threadDecimalSeparator, userDefaultDecimalSeparator);

        return true;
    }
}; wxIMPLEMENT_APP(MyApp);
One more thing: The code above raises the very same error for me! So I can reproduce the issue with this small code sample on a German locale!

Maybe I should report to the wx guys upstream???
Attachments
locale_error.png
locale_error.png (10.73 KiB) Viewed 2958 times
MortenMacFly
Experienced Solver
Experienced Solver
Posts: 73
Joined: Thu Feb 09, 2006 8:13 am

Re: wxArtProvider and Translation

Post by MortenMacFly »

GUYS! Guess what, I figured it out (but I am stills stuck!).

The issue is due to static linking!

Taking the code of the example above the issue does not appear for dynamic linking:

Code: Select all

g++.exe -O2 -pipe -D__GNUWIN32__ -D__WXMSW__ -DWXUSINGDLL -ID:\Devel\wxWidgets31\include -ID:\Devel\wxWidgets31\lib\gcc_dll\mswu -c locale_test.cpp -o locale_test.o            
g++.exe -LD:\Devel\wxWidgets31\lib\gcc_dll -o locale_test_dynamic.exe locale_test.o -mwindows -lwxmsw31u 
...but it does appear for static linking:

Code: Select all

g++.exe -O2 -pipe -D__GNUWIN32__ -D__WXMSW__ -ID:\Devel\wxWidgets31\include -ID:\Devel\wxWidgets31\lib\gcc_lib\mswu -c locale_test.cpp -o locale_test.o            
g++.exe -LD:\Devel\wxWidgets31\lib\gcc_lib -o locale_test_static.exe locale_test.o -mwindows -lwxmsw31u -lwxpng -lwxzlib -lcrypt32 -luuid -lole32 -lcomctl32 -loleaut32 -loleacc -lws2_32 -lwinspool -lcomdlg32 -lgdi32 -lwldap32 -lwinmm -lshlwapi -lversion -luxtheme -lucrtbase 
Can you (PB) do me a favour and try that on your Czech locale if you can reproduce? I am using wxWidgets 3.1.5, compiled monolitic.
ONEEYEMAN
Part Of The Furniture
Part Of The Furniture
Posts: 7459
Joined: Sat Apr 16, 2005 7:22 am
Location: USA, Ukraine

Re: wxArtProvider and Translation

Post by ONEEYEMAN »

Hi,
Can you try and drop monolithic?
The only thing monolithic build is useful is if you plan on developing something for code::blocks. Only!!

Everywhere else it gives more problems than people need and they still follow the stupid YouTube tutorial saying "it works". 😀

Thank you.
PB
Part Of The Furniture
Part Of The Furniture
Posts: 4193
Joined: Sun Jan 03, 2010 5:45 pm

Re: wxArtProvider and Translation

Post by PB »

MortenMacFly wrote: Sat May 15, 2021 5:51 am GUYS! Guess what, I figured it out (but I am stills stuck!).

The issue is due to static linking!
Can you (PB) do me a favour and try that on your Czech locale if you can reproduce? I am using wxWidgets 3.1.5, compiled monolitic.
I used MSVS 2019 (64-bit DLL Debug) for this before, as I did not think it would be compiler related.

I have now tried with GCC and the code above, where I only added

Code: Select all

#include <memory>
which GCC needs for std::shared_ptr to actually build.

Firstly, I used wxWidgets master and 32-bit GCC 10.3 (MSYS2 package mingw-w64-i686-toolchain), as master does not build as monolithic when using wxWebViewEdge and GCC makefile, I built 32-bit debug multilib real static, basically like like this

Code: Select all

mingw32-make -f makefile.gcc CXXFLAGS="-std=c++17" LDFLAGS="-static"
Could not reproduce the issue, i.e., no assert message was shown, regardless whether wxLocale's decimal separator matches Windows one (I have tried both, with wxLocale's set to the comma and point, the Windows' is always the comma).

Secondly, I used wxWidgets 3.1.5 and 64-bit GCC 8.1 (mingw-w64: x86_64-8.1.0-win32-seh-rt_v6-rev0), where I built 64-bit debug monlithic real static build, basically like like this

Code: Select all

mingw32-make -f makefile.gcc MONOLITHIC=1 CFG=-810_x64_mono_staticCRT CXXFLAGS="-std=c++17" LDFLAGS="-static"
So this should be basically the same you have, C::B build log was

Code: Select all

g++.exe -pipe -mthreads -D__GNUWIN32__ -D__WXMSW__ -DwxUSE_UNICODE -Wall -std=c++17 -g -D__WXDEBUG__ -ID:\Dev\Desktop\!Lib\wxWidgets-3.1.5\include -ID:\Dev\Desktop\!Lib\wxWidgets-3.1.5\lib\gcc_lib-810_x64_mono_staticCRT\mswud -c C:\dev\myapps\test-locale-static315-810\test_locale_static315_810App.cpp -o obj\Debug\test_locale_static315_810App.o
g++.exe -LD:\Dev\Desktop\!Lib\wxWidgets-3.1.5\lib\gcc_lib-810_x64_mono_staticCRT -o bin\Debug\test-locale-static315-810.exe  obj\Debug\test_locale_static315_810App.o obj\Debug\resource.res -static -mthreads  -lwxmsw31ud -lwxpngd -lwxjpegd -lwxtiffd -lwxzlibd -lkernel32 -luser32 -lgdi32 -lwinspool -lcomdlg32 -ladvapi32 -lshell32 -lole32 -loleaut32 -luuid -lcomctl32 -lwsock32 -lodbc32 -lshlwapi -lversion -loleacc -luxtheme -mwindows
However, I could not reproduce the issue there neither. I have noticed your Windows libraries list looks slightly different and you also have

Code: Select all

-lucrtbase
there. The libraries in my project are just those added by C::B wxWidgets project wizard. Unlike me, you probably also do not have a "real" static build where one links statically not only to wxWidgets but also to the compiler libraries? You also do not build with C++17 standard but it is not very likely that this has any effect on the issue in question.

I would also try with a truly minimal project as I did, to make sure no other library is interfering.


I am running 64-bit Win10 Pro 20H2 build 19042.985. Language and locale both set to Czech.
Post Reply