Multilangual : how make wxPLURAL work ? 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.
Post Reply
Feneck91
Knows some wx things
Knows some wx things
Posts: 32
Joined: Tue Feb 28, 2006 7:47 am

Multilangual : how make wxPLURAL work ?

Post by Feneck91 » Wed Jul 27, 2011 6:21 am

I'm translating my application and want to make wxPLURAL macro work.
Looking for informations on internet, but not really find how wxPLURAL work.
Using wxWidgets 2.9.2 / Windows Vista / MinGW / Code::Blocks.

1> Configuring poEdit :
1.1> In Catalog/Setttings/Plural forms : set "nplurals=2; plural=(n>1);" to have plural if >1, if 0 it is not plural (in french it is like this, strange to see everywhere nplurals=2; plural=(n!=1); that indicate plural is >1 and 0 (0 car takes a 's' ?)
1.2> In Catalog/Setttings/Keywords : poEdit will not able to find macro wxPLURAL, so we need add it, but if add it only, it will not able to find _. So add "_" AND "wxPLURAL:1,2" (without quote).
2> Add wxPLURAL into your code, like:

Code: Select all

wxLogMessage(wxString::Format(wxPLURAL("%d car","%d cars",5),5));
wxLogMessage(wxString::Format(wxPLURAL("%d car","%d cars",1),1));
wxLogMessage(wxString::Format(wxPLURAL("%d car","%d cars",0),0));
3> In poEdit, synchronize with source code, you will see "%d car" and "%d cars" on se same line, with Singular and plural with 2 translations.
I put "%d voiture" and "%d voitures" for french translation.
Save, compile all and run, it generate :
5 cars
1 car
0 cars
Two errors:
1> The plural doesn't respect the rule "nplurals=2; plural=(n>1);" 0 cars take a 's' and it must not have 's'
2> The translation not work (my software is translated in french, all working fine, all ... but not the plural form).

An idea ?

Feneck91
Knows some wx things
Knows some wx things
Posts: 32
Joined: Tue Feb 28, 2006 7:47 am

Re: Multilangual : how make wxPLURAL work ?

Post by Feneck91 » Wed Jul 27, 2011 6:13 pm

Code: Select all

wxLogMessage(_("%d car"),0);
wxLogMessage(_("%d cars"),8);
Not work too: string that are translated with plural cannot be used to translated: all stay in english.
Is it a wxWidgets problem or a poEdit one ? I hace download the latest poedit software.

User avatar
doublemax
Moderator
Moderator
Posts: 14893
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: Multilangual : how make wxPLURAL work ?

Post by doublemax » Wed Jul 27, 2011 6:22 pm

I have no experience with this, so i don't know an answer.

But i found this comment in "translation.h":

Code: Select all

// gettext() style macros (notice that xgettext should be invoked with
// --keyword="_" --keyword="wxPLURAL:1,2" options
// to extract the strings from the sources)
Maybe this helps.
Use the source, Luke!

Feneck91
Knows some wx things
Knows some wx things
Posts: 32
Joined: Tue Feb 28, 2006 7:47 am

Re: Multilangual : how make wxPLURAL work ?

Post by Feneck91 » Thu Jul 28, 2011 4:28 am

It is explain in "1.2" of my first message.
1.2> In Catalog/Setttings/Keywords : poEdit will not able to find macro wxPLURAL, so we need add it, but if add it only, it will not able to find _. So add "_" AND "wxPLURAL:1,2" (without quote).
For this message:
if 0 it is not plural (in french it is like this, strange to see everywhere nplurals=2; plural=(n!=1); that indicate plural is >1 and 0 (0 car takes a 's' ?)
I found a news (in french) that indicate all rules, 0 cars take a 's' in english, 0 voiture don't take a s in french, rules are differents, that's why it is very important to make wxPLURAL work fine because I cannot put a code like:

Code: Select all

wxString strMyMessage = (n>1) ? _("car") : _("cars")
because this rule is not good for all languages.

For the moment it's don't work. Another strange thing: Writting a good french software in multi language is always writting it in english and translate it into french (if someone want to translate to arabic or chinese, it is more simple to translate from english than from french)... But multilangual software don't need po/mo, if the translation is missing or mo don't exists, the string stay in same language (english if software is written in english), but the rule for plural is into the mo file, what about the wxPLURAL default working way (it seems to be the way I found (n!=1) so by default like in english) ? If no mo and the software is written in french wxPLURAL don"t work ?

Feneck91
Knows some wx things
Knows some wx things
Posts: 32
Joined: Tue Feb 28, 2006 7:47 am

Re: Multilangual : how make wxPLURAL work ?

Post by Feneck91 » Fri Jul 29, 2011 8:59 pm

I have found the answers:
I modify my code to work with wxWidgets 2.8.12 and 2.9.2. In 2.8.12 my application translate nothing!
Why? I use my own lib names wxETK (wxWidgets Extension Toolkit) so I have created a class names wxETKApp.
My application (TTGest) use wxETK but is splitted in two part : a specific lib (dll names TTGestLib) + the GUI (exe names TTGest) to be able (perharps) to port this appli on iPhone, and so develop only GUI part (sure I'll never do this).
In TTGestLib, a specific app names TTGestLibApp is derived from wxETKApp
In TTGest, a specific app names TTGestApp is derived from TTGestLibApp.

The wxLocale is a member of wxETKApp but I forgot to delete old wxLocale member of TTGestLib. I never call Init on it, do nothing... So strange with this forget member :
It translate all but not plural in wxWidgets 2.9.2 and translate nothing with wxWidgets 2.8.12.
Removed it ............ ALL WORK FINE !!

If you want to autodetect language, I let my source code (some class are missing) :
header :

Code: Select all

/////////////////////////////////////////////////////////////////////////////
// Name:        wxETKApp.h
// Library:     wxETK
// Purpose:     Defines Application Class
// Author:      Stéphane Château
// Modified by:
// Created:     22/07/2011
// Licence:     LGPL
/////////////////////////////////////////////////////////////////////////////

#ifndef WXET_KAPP_H
#define WXET_KAPP_H

#include <wx/app.h>
#include "wxETKTypes.h"
#include <list>

/**
 * Application.
 *
 * This is the entry point of the application.
 * This class is used to automatically detect avalaible (and supported) languages.
 * Flags are found here: http://webtrickz.com/free-37-country-flag-icons-in-png-ico-and-icns-format/
 * It first create log file and/or prepare log window.
 *
 * @author Stéphane Château
 * @version Name : wxETK<br>
 *          Revision : <b>1.0</b>
 */
class EXPORT_IMPORT wxETKApp : public wxApp
{
public:
    /**
     * Name of the section where to find languages informations.
     */
    static wxString                     STR_LANGUAGE_SECTION_NAME;

    /**
     * Name of the key where current language is written.
     */
    static wxString                     STR_LANGUAGE_CURRENT_KEY_NAME;

private:
    struct stLanguageInformations
    {
        /**
         * Current language info.
         */
        const wxLanguageInfo *  m_pLanguageInfo;

        /**
         * Current language name (translated).
         */
        wxString                m_strLanguageName;

        /**
         * Tag of the country used (into ini file and folder) like en, fr, de, etc..
         */
        wxString                m_strLanguageTag;

        /**
         * Path of the country flag.
         */
        wxString                m_strPathOfImageFlag;

        /**
         * ID for menu entry.
         */
        long                    m_lID;
    };
protected:
    typedef std::list<stLanguageInformations> tdListAvalaibleLanguages;

    /** Traduction managment.
     *
     * Used for translation automation.
     */
    wxLocale                            m_Locale;

    /**
     * List of all avalaible languages.
     */
    tdListAvalaibleLanguages            m_lstAvalaibleLanguages;

    /**
     * Record the selected language.
     *
     * By default it is the OS language is application support it, else it is "en" (english).
     */
    const stLanguageInformations *      m_pSelectedLanguage;

    /**
     * Used to be called to change current language.
     *
     * @param _rEvent Command event informations.
     */
    virtual void                        OnMenuItemLanguageSelect(wxCommandEvent& _rEvent);

    /**
     * Used to be called to update check language that indicate the current language used.
     *
     * @param _rUpdateUIEvent Update event informations. In fact it is a wxUpdateUIEvent & type but connect
     *                        don't want a function as type wxUpdateUIEventFunction, it would wxObjectEventFunction type.
     */
    virtual void                        OnMenuItemLanguageUpdateUI(wxCommandEvent& _rEvent);


public:
    /// @name Constructor / Destructor.
    //@{
    /**
     * Default constructor.
     */
    wxETKApp();

    /**
     * Destructor.
     */
    virtual ~wxETKApp();
    //@}

    /**
     * Intialise application.
     *
     * true if all is correctly initialize, false else, in this case the
     * application will exit.
     */
    virtual bool                        OnInit();

    /**
     * Create the menu list with all languages avalaible.
     *
     * @param _pMenuLanguage Parent menu to add items to.
     */
    void                                InitializeLanguageMenu(wxMenu *_pMenuLanguage);

    /**
     * Init the current language.
     *
     * Detect all avalaible language, take the OS language by default if exists, else take english.
     * Must be called only once.
     *
     * @param If none avalaible, return false.
     */
    void                                SetCurrentLanguage(const stLanguageInformations *_pLanguage);

    /**
     * Get the real app name.
     *
     * Remove last "d" if present in debug mode.
     *
     * @return The app name.
     */
    wxString                            GetAppNameWithoutDebugTag() const;

protected:
    /**
     * Let the derived class add its own translation catalog.
     *
     * Only call _rLocale.AddCatalog(_T("xxxxx"));
     *
     * @param _rLocale Locale where add catalog.
     */
    virtual void                        AddAppCatalog(wxLocale &_rLocale) = 0;

    /**
     * Indicate if the log file should be created.
     *
     * This function returns always true, you can override it to don't create log file.
     *
     * @return true if the log file should be created or not.
     */
    virtual bool                        ShouldCreateLogFile() const;

    /**
     * Indicate if the log window will be created.
     *
     * If the log window will be created, it create a log recorder to record all log generated before the
     * windows creation to don't miss any log.
     * Never return true if the log window is not created else the logger will record all log and will take a
     * lot of memory.
     *
     * @return true if the log window will be created or not.
     */
    virtual bool                        ShouldCreateLogWindow() const = 0;

    /**
     * Called to be able to change log file configuration.
     *
     * All params are already initialized by default values, set only value that you want change.
     * By default this function does nothing.
     *
     * @param _rbAppend true by default, when true all files are previously destroyed before create new log files.
     * @param _rlNumberOfFiles Number of files to create, by default is 4.
     * @param _rnSizeLimit Size in bytes of each file, by default 1MB (1024 * 1024).
     */
    virtual void                        GetLogFileParameters(bool &_rbAppend,long &_rlNumberOfFiles,fpos_t &_rnSizeLimit);

private:
    /**
     * Add wxETK add its own translation catalog.
     *
     * @param _rLocale Locale where add wxETK catalog.
     */
    void                                AddwxETKAppCatalog(wxLocale &_rLocale);

    /**
     * Append menu item to the menu and create connection for command and updateUI.
     *
     * @param _pMenuLanguage Parent menu to add item to.
     * @param _pLanguageInformations Pointer on language informations.
     */
    void                                AppendMenuItem(wxMenu *_pMenuLanguage,const stLanguageInformations *_pLanguageInformations);

    /**
     * Init all logs.
     *
     * Create the log file and prepare to create the log window.
     * It's load the log informations level from ini file.
     *
     * @return true if no error, false else.
     */
    bool                                InitLogs();

    /**
     * Init the current language.
     *
     * Detect all avalaible language, take the OS language by default if exists, else take english.
     *
     * @return true if none avalaible, return false.
     */
    bool                                InitLanguages();

    /**
     * Detect all avalaible language.
     *
     * English is always an avalaible language.
     */
    void                                InitAllAvalaibleLanguages();


    /**
     * Detect and return the current language that should be used.
     *
     * Detect if the user has defined a specific language into the ini file and get this current language informations.
     * If it is not avalaible, try OS language one. If none avalaible take english one.
     * If none avalaible, return NULL.
     *
     * @return If none avalaible return NULL, else returns the language that should be used as current.
     */
    const stLanguageInformations *      AutoDetectLanguage();

    /**
     * Find the language information by its tag (fr, en de, etc).
     *
     * @param _strLanguageTag Tag to find.
     * @return The pointer on informations if found, NULL else.
     */
    const stLanguageInformations *      FindLanguageByTag(wxString _strLanguageTag) const;

    /**
     * Used to sort list in alphabetical order.
     *
     * @param _rVal1 Value 1.
     * @param _rVal2 Value 2.
     * @return true if _rVal1 < _rVal2, false else.
     */
    static bool                         SortByLanguageName(const stLanguageInformations &_rVal1,const stLanguageInformations &_rVal2);
};

#endif // WXET_KAPP_H
Implementation :

Code: Select all

/////////////////////////////////////////////////////////////////////////////
// Name:        wxETKApp.cpp
// Library:     wxETK
// Purpose:     Implementation of Application Class.
// Author:      Stéphane Château
// Modified by:
// Created:     22/07/2011
// Licence:     LGPL
/////////////////////////////////////////////////////////////////////////////
#include "wxETKApp.h"
#include "wxETKTools.h"
#include "wxETKAutomaton.h"
#include "wxETKLuaManager.h"
#include "wxETKLogWindowPanel.h"
#include "wxETKLogFile.h"
#include <wx/fileconf.h>
#include <wx/utils.h>
#include <wx/dir.h>
#include <wx/renderer.h>

// Use for logger to know the component's name
const char *wxLOG_COMPONENT = "wxETK";

wxString wxETKApp::STR_LANGUAGE_SECTION_NAME        = _T("/LANGUAGE");
wxString wxETKApp::STR_LANGUAGE_CURRENT_KEY_NAME    = _T("Current");

// Difference with 2.8.x version and later
#if !wxCHECK_VERSION(2,9,0)
    #define wxLOCALE_DONT_LOAD_DEFAULT ((wxLocaleInitFlags) 0)
#endif

wxETKApp::wxETKApp()
    : m_pSelectedLanguage(NULL)
{
}

wxETKApp::~wxETKApp()
{
}

bool wxETKApp::OnInit()
{
    // Set current path same as EXE
    wxSetWorkingDirectory(wxETKTools::GetExePath());

    wxString strAppName(GetAppNameWithoutDebugTag());

    // Compute ini file path
    wxFileName fnIniFilename;
    fnIniFilename.Assign(wxETKTools::GetExePath(),strAppName+_T(".ini"),wxPATH_NATIVE);

    // Configuration file
    wxConfigBase *pOldConfig = wxConfigBase::Set(new wxFileConfig(fnIniFilename.GetFullName(),wxEmptyString,wxEmptyString,fnIniFilename.GetFullPath()));
    wxDELETE(pOldConfig); // Delete old one if not null

    if (!InitLogs())
    {
        return false;
    }
    // Multilanguages manager
    bool bRet = InitLanguages();

    return bRet;
}

bool wxETKApp::InitLogs()
{
    // Compute log file path
    // Log managment
    // Initialize log format: DATE + TIME + ms
    wxLog::SetTimestamp(_T("%x %X.%l"));

    // While InitGUI is not called, put all trace level to verbose + lowest level

#ifdef __WXDEBUG__
    wxLog::SetVerbose(true);
    wxLog::SetLogLevel(wxLOG_Debug);
#else
    wxLog::SetVerbose(false);
    wxLog::SetLogLevel(wxLOG_Info);
#endif

    // Add Log mask for automaton / lua
    wxLog::AddTraceMask(wxETKAutomatonManager::STR_LOG_TRACE_MASK);
    wxLog::AddTraceMask(wxETKLuaManager_STR_LOG_TRACE_MASK);
    wxString strAppName(GetAppNameWithoutDebugTag());

    if (ShouldCreateLogWindow())
    {
        wxLog *pOldLog = wxLog::SetActiveTarget(wxETKLogWindowPanel::StartLogRecordWaitingWindowsCreation());
        wxDELETE(pOldLog); // Delete old log
    }

    //----------------------------
    // Logs initialisations
    if (ShouldCreateLogFile())
    {
        wxETKLogFile *pLogFile;
        bool bAppend        = true;
        long lNumberOfFiles = 4;
        fpos_t nSizeLimit  = 1024 * 1024;
        GetLogFileParameters(bAppend,lNumberOfFiles,nSizeLimit);
        if (ShouldCreateLogWindow())
        {    // If ShouldCreateLogWindow() is true, wxLog::SetActiveTarget has already been called
            new wxLogChain(pLogFile = new wxETKLogFile(strAppName+_T(".log"),false,bAppend,lNumberOfFiles,nSizeLimit)); // deleted by framework when app is closed
        }
        else
        {
            wxLog *pOldLog = wxLog::SetActiveTarget(pLogFile = new wxETKLogFile(strAppName+_T(".log"),false,bAppend,lNumberOfFiles,nSizeLimit)); // deleted by framework when app is closed
            wxDELETE(pOldLog); // Delete old log
        }

        pLogFile->SetStoppingApplicationLog(wxString::Format(wxT("%s\nApplication %s is stopped\n%s"),
                                            wxT("--------------------------------------------------------------"),
                                            strAppName.wx_str(),
                                            wxT("--------------------------------------------------------------"))
                                           );
    }
    // END - Logs initialisations
    //----------------------------

    wxLogMessage(wxString::Format(wxT("%s\nStarting %s application\n%s"),
                 wxT("--------------------------------------------------------------"),
                 strAppName.wx_str(),
                 wxT("--------------------------------------------------------------"))
                );

    // Init GUI + Load trace level
    if (!wxETKTools::InitGUI())
    {
        return false;
    }

    return true;
}

bool wxETKApp::InitLanguages()
{
    bool bRet = false;
    wxLocale::AddCatalogLookupPathPrefix(_T("locale"));
    InitAllAvalaibleLanguages();

    //----------------------
    // Multilanguages manager
    const stLanguageInformations * pCurrentAutoDetectLanguage = AutoDetectLanguage();
    if (pCurrentAutoDetectLanguage != NULL)
    {
        bRet = true;
        SetCurrentLanguage(pCurrentAutoDetectLanguage);
    }
    // Multilanguages manager
    //----------------------
    return bRet;
}

const wxETKApp::stLanguageInformations * wxETKApp::AutoDetectLanguage()
{
    const stLanguageInformations *pSelectedLanguage = NULL;
    wxConfigBase::Get()->SetPath(STR_LANGUAGE_SECTION_NAME);

    wxString strCurrentLanguage = wxConfigBase::Get()->Read(STR_LANGUAGE_CURRENT_KEY_NAME,wxEmptyString);
    if (!strCurrentLanguage.IsEmpty())
    {   // Take this one (read from ini file)
        pSelectedLanguage = FindLanguageByTag(strCurrentLanguage);
        if (pSelectedLanguage == NULL)
        {
            wxLogWarning(wxT("Language tag '%s' is not avalaible, ignored it!"),strCurrentLanguage.wx_str());
        }
    }

    if (pSelectedLanguage == NULL)
    {   // Take the language of the current OS if avalaible
        wxLocale locale;
        // Try to find if the current OS language could be used
        if (locale.Init(wxLANGUAGE_DEFAULT,wxLOCALE_DONT_LOAD_DEFAULT))
        {
            for (tdListAvalaibleLanguages::iterator it=m_lstAvalaibleLanguages.begin();it!=m_lstAvalaibleLanguages.end();++it)
            {
                if (locale.GetLanguage() == (*it).m_pLanguageInfo->Language)
                {   // Ok, found it
                    pSelectedLanguage = &(*it);
                    break;
                }
            }
        }
    }

    // Not found, take english default one
    if (pSelectedLanguage == NULL)
    {   // Take english one
        pSelectedLanguage = FindLanguageByTag(_T("en"));
    }

    return pSelectedLanguage;
}

void wxETKApp::InitializeLanguageMenu(wxMenu *_pMenuLanguage)
{
    wxASSERT(m_pSelectedLanguage != NULL);

    if (m_pSelectedLanguage != NULL)
    {
        AppendMenuItem(_pMenuLanguage,m_pSelectedLanguage);
        _pMenuLanguage->AppendSeparator();
    }

    for (tdListAvalaibleLanguages::const_iterator it=m_lstAvalaibleLanguages.begin();it!=m_lstAvalaibleLanguages.end();++it)
    {
        if ((*it).m_lID != m_pSelectedLanguage->m_lID)
        {
            AppendMenuItem(_pMenuLanguage,&(*it));
        }
    }
}

/*
void DrawCheckMark(wxWindow *_pWindows,wxDC &_rDC,wxCoord x1, wxCoord y1,wxCoord width, wxCoord height)
{
    wxCoord x2 = x1 + width,
            y2 = y1 + height;

    // the pen width is calibrated to give 3 for width == height == 10
    //wxPen pen(wxSystemSettings::GetColour(wxSYS_COLOUR_MENUBAR), (width + height + 1)/7);
    wxPen pen(wxColor(0,255,0), (width + height + 1)/7);
    _rDC.SetPen(pen);

    // we're drawing a scaled version of wx/generic/tick.xpm here
    wxCoord x3 = x1 + (4*width) / 10,   // x of the tick bottom
            y3 = y1 + height / 2;       // y of the left tick branch
    _rDC.DrawLine(x1, y3, x3, y2);
    _rDC.DrawLine(x3, y2, x2, y1);

    _rDC.SetPen(wxNullPen);

}


for (tdListAvalaibleLanguages::const_iterator it=m_lstAvalaibleLanguages.begin();it!=m_lstAvalaibleLanguages.end();++it)
{
    wxMenuItem *pItemMenu = new wxMenuItem(NULL,(*it).m_lID, (*it).m_strLanguageName, wxEmptyString,wxITEM_CHECK);
    wxASSERT(wxFile::Exists((*it).m_strPathOfImageFlag)); // Missing image ?
    if (wxFile::Exists((*it).m_strPathOfImageFlag))
    {
        wxBitmap imgFlag((*it).m_strPathOfImageFlag,wxBITMAP_TYPE_PNG);
        //wxBitmap imgFlagChecked((*it).m_strPathOfImageFlag,wxBITMAP_TYPE_PNG);
        wxBitmap imgFlagChecked(16,16,wxBITMAP_SCREEN_DEPTH);
        {   // <-- Begin of bloc for wxMemoryDC, don't remove this bloc
            //.
            wxMemoryDC dc(imgFlagChecked);
            //dc.SelectObject(imgFlagChecked);
            //wxRendererNative::GetDefault().DrawCheckBox(GetTopWindow(),dc,wxRect(0,0,16,16),wxCONTROL_CHECKED);
            dc.SetBrush(wxBrush(wxColor(255,0,0)));
            dc.DrawRectangle(0,0,16,16);
            dc.SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_MENUTEXT), (32 + 1)/7));
            dc.SetBrush( *wxTRANSPARENT_BRUSH );
            //dc.DrawCheckMark(wxRect(0,0,16,16));
            dc.SetPen(wxNullPen);
            DrawCheckMark(GetTopWindow(),dc,3,3,10,10);
// WIN32 - ::DrawFrameControl(hdcMem, &rect, DFC_MENU, DFCS_MENUCHECK);
        }

        pItemMenu->SetBitmaps(imgFlagChecked,imgFlag);
    }
    _pMenuLanguage->Append(pItemMenu);
    // Connect update UI + Command events.
    Connect((*it).m_lID,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&wxETKApp::OnMenuItemLanguageSelect);
    //Connect((*it).m_lID,wxEVT_UPDATE_UI,(wxUpdateUIEventFunction)&wxETKApp::OnMenuItemLanguageUpdateUI); <- Connect don't want
    Connect((*it).m_lID,wxEVT_UPDATE_UI,(wxObjectEventFunction)&wxETKApp::OnMenuItemLanguageUpdateUI);
}
*/

void wxETKApp::AppendMenuItem(wxMenu *_pMenuLanguage,const stLanguageInformations *_pLanguageInformations)
{
   wxMenuItem *pItemMenu = new wxMenuItem(_pMenuLanguage,_pLanguageInformations->m_lID, _pLanguageInformations->m_strLanguageName, wxEmptyString,wxITEM_NORMAL); //wxITEM_CHECK
    wxASSERT(wxFile::Exists(_pLanguageInformations->m_strPathOfImageFlag)); // Missing image ?
    if (wxFile::Exists(_pLanguageInformations->m_strPathOfImageFlag))
    {
        wxBitmap imgFlag(_pLanguageInformations->m_strPathOfImageFlag,wxBITMAP_TYPE_PNG);
        pItemMenu->SetBitmaps(imgFlag,imgFlag);
    }
    _pMenuLanguage->Append(pItemMenu);
    // Connect update UI + Command events.
    Connect(_pLanguageInformations->m_lID,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&wxETKApp::OnMenuItemLanguageSelect);
    //Connect((*it).m_lID,wxEVT_UPDATE_UI,(wxUpdateUIEventFunction)&wxETKApp::OnMenuItemLanguageUpdateUI); <- Connect don't want
    Connect(_pLanguageInformations->m_lID,wxEVT_UPDATE_UI,(wxObjectEventFunction)&wxETKApp::OnMenuItemLanguageUpdateUI);
}

void wxETKApp::OnMenuItemLanguageSelect(wxCommandEvent& _rEvent)
{
    wxConfigBase::Get()->SetPath(STR_LANGUAGE_SECTION_NAME);

    for (tdListAvalaibleLanguages::const_iterator it=m_lstAvalaibleLanguages.begin();it!=m_lstAvalaibleLanguages.end();++it)
    {
        if ((*it).m_lID == _rEvent.GetId())
        {   // Ok, found it
            wxLocale locale;
            if (locale.Init((*it).m_pLanguageInfo->Language, wxLOCALE_DONT_LOAD_DEFAULT))
            {
                // Add application
                AddAppCatalog(locale);
                AddwxETKAppCatalog(locale);

                wxMessageBox(_("Language will be changed only when application the will be restarted."),_("Language has been changed"),wxOK | wxCENTRE | wxICON_INFORMATION);
                wxConfigBase::Get()->Write(STR_LANGUAGE_CURRENT_KEY_NAME,(*it).m_strLanguageTag);
            }
            break;
        }
    }
}

void wxETKApp::OnMenuItemLanguageUpdateUI(wxCommandEvent& _rEvent)
{
    wxASSERT(m_pSelectedLanguage != NULL);

    // Cast wxCommandEvent to wxUpdateUIEvent
    wxUpdateUIEvent *pUpdateUIEvent = dynamic_cast<wxUpdateUIEvent *>(&_rEvent);
    wxASSERT(pUpdateUIEvent != NULL);

    if (m_pSelectedLanguage != NULL && pUpdateUIEvent != NULL)
    {   // If menu item created is wxITEM_CHECK but for the moment it is wxITEM_NORMAL
        //pUpdateUIEvent->Check(pUpdateUIEvent->GetId() == m_pSelectedLanguage->m_lID); // Check current language
    }
}

void wxETKApp::AddwxETKAppCatalog(wxLocale &_rLocale)
{
    _rLocale.AddCatalog(_T("wxETK"));
    _rLocale.AddCatalog(WX_STD_MO_NAME); // wxWidgets translation

    // 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 __LINUX__
    {
        wxLogNull noLog;
        _rLocale.AddCatalog(_T("fileutils"));
    }
#endif
}

void wxETKApp::SetCurrentLanguage(const stLanguageInformations *_pLanguage)
{
    wxASSERT(_pLanguage != NULL);
    m_pSelectedLanguage = _pLanguage;

    if (m_pSelectedLanguage != NULL)
    {
        m_Locale.Init(_pLanguage->m_pLanguageInfo->Language,wxLOCALE_LOAD_DEFAULT);
        // Initialisation of the class with default language
        // Add catalog (*.mo)
        AddAppCatalog(m_Locale);
        AddwxETKAppCatalog(m_Locale);
        wxLogMessage(wxT("Set language to %s (%s)."),_pLanguage->m_pLanguageInfo->Description.wx_str(),_pLanguage->m_strLanguageName.wx_str());
    }
}

wxString wxETKApp::GetAppNameWithoutDebugTag() const
{
    wxString strAppName(GetAppName());
    #ifdef __WXDEBUG__
    if (strAppName.Right(1).Upper() == _T("D"))
    {   // d for debug, remove d at the end of exe for ini file
        strAppName = strAppName.Left(strAppName.Length() - 1);
    }
    #endif
    return strAppName;
}

bool wxETKApp::ShouldCreateLogFile() const
{
    return true;
}

void wxETKApp::GetLogFileParameters(bool &_rbAppend,long &_rlNumberOfFiles,fpos_t &_rnSizeLimit)
{
    // Nothing to do
}

const wxETKApp::stLanguageInformations * wxETKApp::FindLanguageByTag(wxString _strLanguageTag) const
{
    const stLanguageInformations *pLanguageInformationsRet = NULL;
    for (tdListAvalaibleLanguages::const_iterator it=m_lstAvalaibleLanguages.begin();it!=m_lstAvalaibleLanguages.end();++it)
    {
        if ((*it).m_strLanguageTag == _strLanguageTag)
        {   // Ok, found it
            pLanguageInformationsRet = &(*it);
            break;
        }
    }

    return pLanguageInformationsRet;
}

// Used to sort list in alphabetical order
bool wxETKApp::SortByLanguageName(const stLanguageInformations &_rVal1,const stLanguageInformations &_rVal2)
{
    return _rVal1.m_strLanguageName < _rVal2.m_strLanguageName;
}

void wxETKApp::InitAllAvalaibleLanguages()
{
    wxString strDirNameRoot(wxETKTools::GetExePath() + _T("locale") + wxFileName::GetPathTerminators(wxPATH_NATIVE)),strDirName;
    int iLenExePath = strDirNameRoot.Len();
    wxDir dir(strDirNameRoot);
    bool bContinue = dir.GetFirst(&strDirName, wxEmptyString, wxDIR_DIRS);
    bool bEnglishFound = false; // In case "en" folder was created to replace bad english message (patch ?)
    while ( bContinue )
    {
        wxString strLanguage = strDirName.Right(strDirName.Len() - iLenExePath);
        if (strLanguage.Find(wxFileName::GetPathTerminators(wxPATH_NATIVE).c_str()) == wxNOT_FOUND)
        {   // Not a subdirectory
            stLanguageInformations informations;
            informations.m_pLanguageInfo = wxLocale::FindLanguageInfo(strLanguage);
            if (informations.m_pLanguageInfo != NULL)
            {
                // don't use wxLOCALE_LOAD_DEFAULT flag so that Init() doesn't return
                // false just because it failed to load wxstd catalog
                wxLocale locale;
                if ( locale.Init(informations.m_pLanguageInfo->Language, wxLOCALE_DONT_LOAD_DEFAULT) )
                {
                    if (wxLocale::IsAvailable(informations.m_pLanguageInfo->Language))
                    {
                        // Add application
                        AddAppCatalog(locale);
                        informations.m_strLanguageName = wxGetTranslation(_T("LANGUAGE_NAME"));
                        if (informations.m_strLanguageName != _T("LANGUAGE_NAME"))
                        {   // This language is avalaible
                            if (strLanguage==_T("en"))
                            {
                                // In this case english must be translated, it's override default
                                // language.
                                bEnglishFound = true;
                                wxLogMessage(wxT("'%s' override default english language."),strLanguage.wx_str());
                            }
                            else
                            {
                                wxLogMessage(wxT("'%s' language found: %s (%s)."),strLanguage.wx_str(),informations.m_pLanguageInfo->Description.wx_str(),informations.m_strLanguageName.wx_str());
                            }
                            informations.m_strPathOfImageFlag = wxETKTools::AppendFilePath(strDirNameRoot,strLanguage + wxFileName::GetPathTerminators(wxPATH_NATIVE) + strLanguage + _T(".png")).GetFullPath();
                            informations.m_strLanguageTag     = strLanguage;
                            informations.m_lID                = wxNewId();
                            m_lstAvalaibleLanguages.push_back(informations); // This one is allowed<
                        }
                        else
                        {   // The folder exists, but it doesn't contains translation LANGUAGE_NAME : ignore it, it is mandatory
                            // LANGUAGE_NAME is not translated by wxETK, it must be translated by application mo file because if
                            // the application doesn't want to support french (wxETK support it), the macro _() will find it but all application
                            // will not be translated!
                            wxLogWarning(wxT("'%s' language is found and avalaible but LANGUAGE_NAME is not translated.\n"
                                             "LANGUAGE_NAME is mandatory into mo file to be taken into account, ignored"),strLanguage.wx_str());
                        }
                    }
                    else
                    {   // This language is not avalaible
                        wxLogWarning(wxT("'%s' language is not avalaible, ignored."),strLanguage.wx_str());
                    }
                }
                else
                {   // This language is not supported by the system
                    wxLogWarning(wxT("'%s' is not supported by the system, ignored!"),strLanguage.wx_str());
                }
            }
            else
            {   // This language is not an avalaible language
                wxLogVerbose(wxT("'%s' is not an avalaible language, ignored."),strLanguage.wx_str());
            }
        }
        else
        {
            wxLogVerbose(wxT("'%s' is subdirectory, ignored."),strLanguage.wx_str());
        }
        bContinue = dir.GetNext(&strDirName);
    }
    if (!bEnglishFound)
    {   // Add english default
        stLanguageInformations informations;
        informations.m_pLanguageInfo      = wxLocale::FindLanguageInfo(_T("en"));
        // The default english flag is not into locale/en/en.png because by default the en folder doesn't exist
        // So, en.png is into "locale/img/en.png", it is the only one that doesn't record into the language folder.
        informations.m_strPathOfImageFlag = wxETKTools::AppendFilePath(strDirNameRoot,_T("img") + wxFileName::GetPathTerminators(wxPATH_NATIVE) + wxString(_T("en.png"))).GetFullPath();
        informations.m_strLanguageName    = _T("English");   // Don't translate english to anglais, it is not good, all language
                                                                // names must appears in their own languages
        informations.m_strLanguageTag     = _T("en");        // Tag folder
        informations.m_lID                = wxNewId();
        m_lstAvalaibleLanguages.push_back(informations);        // English is always allowed
    }

    // Sort by language names
    m_lstAvalaibleLanguages.sort(SortByLanguageName);
}


maximand
Experienced Solver
Experienced Solver
Posts: 72
Joined: Fri Nov 11, 2011 5:44 pm
Location: Russia

Re: Multilangual : how make wxPLURAL work ?

Post by maximand » Thu Apr 11, 2019 12:19 pm

M$, VS2017, C++

Post Reply