Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Manager] Show native names for language options, and only those for which translations are available #5176

Merged
merged 13 commits into from
May 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 117 additions & 20 deletions clientgui/BOINCGUIApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ bool CBOINCGUIApp::OnInit() {
m_iRPCPortArg = GUI_RPC_PORT;
m_strBOINCArguments = wxEmptyString;
m_strISOLanguageCode = wxEmptyString;
m_bUseDefaultLocale = true;
m_bGUIVisible = true;
m_bDebugSkins = false;
m_bMultipleInstancesOK = false;
Expand All @@ -120,7 +121,7 @@ bool CBOINCGUIApp::OnInit() {
m_bNeedRunDaemon = true;

// Initialize local variables
int iDesiredLanguageCode = 0;
int iDesiredLanguageCode = wxLANGUAGE_DEFAULT;
bool bOpenEventLog = false;
wxString strDesiredSkinName = wxEmptyString;
#ifdef SANDBOX
Expand Down Expand Up @@ -198,6 +199,10 @@ bool CBOINCGUIApp::OnInit() {
#endif
m_pConfig->Read(wxT("DisableAutoStart"), &m_iBOINCMGRDisableAutoStart, 0L);
m_pConfig->Read(wxT("LanguageISO"), &m_strISOLanguageCode, wxT(""));
bool bUseDefaultLocaleDefault =
// Migration: assume a selected language code that matches the system default means "auto select"
m_strISOLanguageCode == wxLocale::GetLanguageInfo(wxLANGUAGE_DEFAULT)->CanonicalName;
m_pConfig->Read(wxT("UseDefaultLocale"), &m_bUseDefaultLocale, bUseDefaultLocaleDefault);
m_pConfig->Read(wxT("GUISelection"), &m_iGUISelected, BOINC_SIMPLEGUI);
m_pConfig->Read(wxT("EventLogOpen"), &bOpenEventLog);
m_pConfig->Read(wxT("RunDaemon"), &m_bRunDaemon, 1L);
Expand Down Expand Up @@ -267,12 +272,15 @@ bool CBOINCGUIApp::OnInit() {
wxASSERT(m_pLocale);

//
if (m_strISOLanguageCode.IsEmpty()) {
iDesiredLanguageCode = wxLANGUAGE_DEFAULT;
m_pLocale->Init(iDesiredLanguageCode);
if (!m_bUseDefaultLocale && !m_strISOLanguageCode.IsEmpty()) {
const wxLanguageInfo* pLI = wxLocale::FindLanguageInfo(m_strISOLanguageCode);
if (pLI) {
iDesiredLanguageCode = pLI->Language;
}
}
m_pLocale->Init(iDesiredLanguageCode);
if (iDesiredLanguageCode == wxLANGUAGE_DEFAULT) {
m_strISOLanguageCode = m_pLocale->GetCanonicalName();
} else {
m_pLocale->Init(wxLocale::FindLanguageInfo(m_strISOLanguageCode)->Language);
}

// Look for the localization files by absolute and relative locations.
Expand Down Expand Up @@ -598,6 +606,7 @@ void CBOINCGUIApp::SaveState() {
m_pConfig->Write(wxT("Skin"), m_pSkinManager->GetSelectedSkin());
}
m_pConfig->Write(wxT("LanguageISO"), m_strISOLanguageCode);
m_pConfig->Write(wxT("UseDefaultLocale"), m_bUseDefaultLocale);
m_pConfig->Write(wxT("AutomaticallyShutdownClient"), m_iShutdownCoreClient);
m_pConfig->Write(wxT("DisplayShutdownClientDialog"), m_iDisplayExitDialog);
m_pConfig->Write(wxT("DisplayShutdownConnectedClientDialog"), m_iDisplayShutdownConnectedClientDialog);
Expand Down Expand Up @@ -893,22 +902,110 @@ void CBOINCGUIApp::DetectDataDirectory() {


void CBOINCGUIApp::InitSupportedLanguages() {
wxInt32 iIndex = 0;
const wxLanguageInfo* liLanguage = NULL;

// Prepare the array
m_astrLanguages.Insert(wxEmptyString, 0, wxLANGUAGE_USER_DEFINED+1);

// These are just special tags so deal with them in a special way
m_astrLanguages[wxLANGUAGE_DEFAULT] = _("(Automatic Detection)");
m_astrLanguages[wxLANGUAGE_UNKNOWN] = _("(Unknown)");
m_astrLanguages[wxLANGUAGE_USER_DEFINED] = _("(User Defined)");
m_astrLanguages.clear();

// Find available translations
std::vector<const wxLanguageInfo*> availableTranslations;
// English is a special case:
// - it's guaranteed to be available because it's compiled in
// - it must be added to the list even though we don't expect to find a translation for it
const wxLanguageInfo* pLIen = wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH);
if (pLIen) {
availableTranslations.push_back(pLIen);
}
// Now fill in the rest from the available message catalogs
const wxTranslations* pTranslations = wxTranslations::Get();
if (pTranslations) {
wxArrayString langCodes = pTranslations->GetAvailableTranslations(wxT("BOINC-Manager"));
for (const wxString& langCode : langCodes) {
if (langCode == wxT("en")) continue;
const wxLanguageInfo* pLI = wxLocale::FindLanguageInfo(langCode);
if (pLI) {
availableTranslations.push_back(pLI);
}
}
}

for (iIndex = 0; iIndex <= wxLANGUAGE_USER_DEFINED; iIndex++) {
liLanguage = wxLocale::GetLanguageInfo(iIndex);
if (liLanguage) {
m_astrLanguages[iIndex] = liLanguage->Description;
// Synthesize labels to be used in the options dialog
//
// As we are building strings that potentially contain both left-to-right and
// right-to-left text, we must insert direction markers to ensure the layout is
// correct. Otherwise strange things happen - particularly when the strings contain
// parentheses, which can end up in the wrong place and pointing the wrong way.
// The usage here has been determined largely by trial and error, and may not be
// strictly correct...
const wxString LRM = L'\x200E'/*LEFT-TO-RIGHT MARK*/;
const wxString RLM = L'\x200F'/*RIGHT-TO-LEFT MARK*/;
const wxLanguageInfo* pLIui = wxLocale::FindLanguageInfo(GetISOLanguageCode());
wxLayoutDirection uiLayoutDirection = pLIui ? pLIui->LayoutDirection : wxLayout_Default;
GUI_SUPPORTED_LANG newItem;

// CDlgOptions depends on "Auto" being the first item in the list
newItem.Language = wxLANGUAGE_DEFAULT;
wxString strAutoEnglish = wxT("(Automatic Detection)");
wxString strAutoTranslated = wxGetTranslation(strAutoEnglish);
newItem.Label = strAutoTranslated;
if (strAutoTranslated != strAutoEnglish) {
if (uiLayoutDirection == wxLayout_RightToLeft) {
newItem.Label += RLM;
} else if (uiLayoutDirection == wxLayout_LeftToRight) {
newItem.Label += LRM;
}
newItem.Label += wxT(" ");
if (uiLayoutDirection == wxLayout_RightToLeft) {
newItem.Label += RLM;
}
newItem.Label += LRM + strAutoEnglish + LRM;
}
m_astrLanguages.push_back(newItem);

// Add known locales to the list
for (int langID = wxLANGUAGE_UNKNOWN+1; langID < wxLANGUAGE_USER_DEFINED; ++langID) {
const wxLanguageInfo* pLI = wxLocale::GetLanguageInfo(langID);
wxString lang_region = pLI->CanonicalName.BeforeFirst('@');
wxString lang = lang_region.BeforeFirst('_');
wxString script = pLI->CanonicalName.AfterFirst('@');
wxString lang_script = lang;
if (!script.empty()) {
lang_script += wxT("@") + script;
}
std::vector<const wxLanguageInfo*>::const_iterator foundit = availableTranslations.begin();
while (foundit != availableTranslations.end()) {
const wxLanguageInfo* pLIavail = *foundit;
if (pLIavail->CanonicalName == lang_script ||
pLIavail->CanonicalName == pLI->CanonicalName) {
break;
}
++foundit;
}
// If we don't have a translation, don't add to the list -
// unless the locale has been explicitly selected by the user
// (setting migrated from an earlier version, or manually configured)
if (foundit == availableTranslations.end() && pLI != pLIui) continue;
newItem.Language = langID;
#if wxCHECK_VERSION(3,1,6)
BrianNixon marked this conversation as resolved.
Show resolved Hide resolved
if (pLI->DescriptionNative != pLI->Description &&
!pLI->DescriptionNative.empty()) {
// The "NativeName (EnglishName)" format of the label matches that used
// for Web sites [language_select() in html/inc/language_names.inc]
newItem.Label = pLI->DescriptionNative;
if (pLI->LayoutDirection == wxLayout_RightToLeft) {
newItem.Label += RLM;
} else if (pLI->LayoutDirection == wxLayout_LeftToRight) {
newItem.Label += LRM;
}
newItem.Label += wxT(" ");
if (uiLayoutDirection == wxLayout_RightToLeft) {
newItem.Label += RLM;
}
newItem.Label += LRM + wxT("(") + pLI->Description + wxT(")") + LRM;
} else {
newItem.Label = pLI->Description + LRM;
}
#else
newItem.Label = pLI->Description + LRM;
#endif
m_astrLanguages.push_back(newItem);
}
}

Expand Down
16 changes: 11 additions & 5 deletions clientgui/BOINCGUIApp.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2016 University of California
// Copyright (C) 2023 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
Expand Down Expand Up @@ -45,6 +45,11 @@ class CSkinManager;
class CDlgEventLog;
class CRPCFinishedEvent;

struct GUI_SUPPORTED_LANG {
int Language; // wxLanguage ID, used to set the locale
wxString Label; // Text to display in the options dialog
};

#ifdef __WXMAC__
OSErr QuitAppleEventHandler(const AppleEvent *appleEvt, AppleEvent* reply, UInt32 refcon);
#endif
Expand Down Expand Up @@ -117,10 +122,9 @@ class CBOINCGUIApp : public wxApp {
bool m_bRunDaemon;
bool m_bNeedRunDaemon;

// The last value defined in the wxLanguage enum is wxLANGUAGE_USER_DEFINED.
// defined in: wx/intl.h
wxArrayString m_astrLanguages;
std::vector<GUI_SUPPORTED_LANG> m_astrLanguages;
wxString m_strISOLanguageCode;
bool m_bUseDefaultLocale;

int m_bSafeMessageBoxDisplayed;

Expand Down Expand Up @@ -187,10 +191,12 @@ class CBOINCGUIApp : public wxApp {
bool GetNeedRunDaemon()
{ return m_bNeedRunDaemon; }

wxArrayString& GetSupportedLanguages() { return m_astrLanguages; }
const std::vector<GUI_SUPPORTED_LANG>& GetSupportedLanguages() const { return m_astrLanguages; }
wxString GetISOLanguageCode() { return m_strISOLanguageCode; }
void SetISOLanguageCode(wxString strISOLanguageCode)
{ m_strISOLanguageCode = strISOLanguageCode; }
bool UseDefaultLocale() const { return m_bUseDefaultLocale; }
void SetUseDefaultLocale(bool b) { m_bUseDefaultLocale = b; }

void SetEventLogWasActive(bool wasActive) { m_bEventLogWasActive = wasActive; }
void DisplayEventLog(bool bShowWindow = true);
Expand Down
48 changes: 41 additions & 7 deletions clientgui/DlgOptions.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
// Copyright (C) 2023 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
Expand Down Expand Up @@ -183,9 +183,13 @@ void CDlgOptions::CreateControls() {
itemStaticText7->Create( itemPanel4, wxID_STATIC, _("Language:"), wxDefaultPosition, wxDefaultSize, 0 );
itemFlexGridSizer6->Add(itemStaticText7, 0, wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL|wxALL, 5);

wxString* m_LanguageSelectionCtrlStrings = NULL;
const std::vector<GUI_SUPPORTED_LANG>& langs = wxGetApp().GetSupportedLanguages();
wxArrayString langLabels;
for (const GUI_SUPPORTED_LANG& lang : langs) {
langLabels.push_back(lang.Label);
}
m_LanguageSelectionCtrl = new wxComboBox;
m_LanguageSelectionCtrl->Create( itemPanel4, ID_LANGUAGESELECTION, wxT(""), wxDefaultPosition, wxDefaultSize, 0, m_LanguageSelectionCtrlStrings, wxCB_READONLY );
m_LanguageSelectionCtrl->Create( itemPanel4, ID_LANGUAGESELECTION, wxT(""), wxDefaultPosition, wxDefaultSize, langLabels, wxCB_READONLY );
if (ShowToolTips())
m_LanguageSelectionCtrl->SetToolTip(_("What language should BOINC use?"));
itemFlexGridSizer6->Add(m_LanguageSelectionCtrl, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5);
Expand Down Expand Up @@ -620,8 +624,24 @@ bool CDlgOptions::ReadSettings() {


// General Tab
m_LanguageSelectionCtrl->Append(wxGetApp().GetSupportedLanguages());
m_LanguageSelectionCtrl->SetSelection(wxLocale::FindLanguageInfo(wxGetApp().GetISOLanguageCode())->Language);
if (wxGetApp().UseDefaultLocale()) {
// CBOINCGUIApp::InitSupportedLanguages() ensures "Auto" is the first item in the list
m_LanguageSelectionCtrl->SetSelection(0);
} else {
const wxLanguageInfo* pLI = wxLocale::FindLanguageInfo(wxGetApp().GetISOLanguageCode());
if (pLI) {
const std::vector<GUI_SUPPORTED_LANG>& langs = wxGetApp().GetSupportedLanguages();
for (std::vector<GUI_SUPPORTED_LANG>::const_iterator foundit = langs.begin();
foundit != langs.end(); ++foundit) {
const GUI_SUPPORTED_LANG& item = *foundit;
if (item.Language == pLI->Language) {
int selLangIdx = std::distance(langs.begin(), foundit);
m_LanguageSelectionCtrl->SetSelection(selLangIdx);
break;
}
}
}
}

m_ReminderFrequencyCtrl->Append(_("always"));
m_ReminderFrequencyCtrl->Append(_("1 hour"));
Expand Down Expand Up @@ -723,7 +743,20 @@ bool CDlgOptions::SaveSettings() {


// General Tab
if (wxLocale::FindLanguageInfo(wxGetApp().GetISOLanguageCode())->Language != m_LanguageSelectionCtrl->GetSelection()) {
wxString oldLangCode = wxGetApp().GetISOLanguageCode();
wxString newLangCode = oldLangCode;
int selLangIdx = m_LanguageSelectionCtrl->GetSelection();
if (selLangIdx == 0) {
// CBOINCGUIApp::InitSupportedLanguages() ensures "Auto" is the first item in the list
newLangCode = wxLocale::GetLanguageInfo(wxLANGUAGE_DEFAULT)->CanonicalName;
} else if (selLangIdx > 0) {
const std::vector<GUI_SUPPORTED_LANG>& langs = wxGetApp().GetSupportedLanguages();
if (selLangIdx < langs.size()) {
const GUI_SUPPORTED_LANG& selLang = langs[selLangIdx];
newLangCode = wxLocale::GetLanguageInfo(selLang.Language)->CanonicalName;
}
}
if (newLangCode != oldLangCode) {
wxString strDialogTitle;
wxString strDialogMessage;

Expand All @@ -749,7 +782,8 @@ bool CDlgOptions::SaveSettings() {
);
}

wxGetApp().SetISOLanguageCode(wxLocale::GetLanguageInfo(m_LanguageSelectionCtrl->GetSelection())->CanonicalName);
wxGetApp().SetISOLanguageCode(newLangCode);
wxGetApp().SetUseDefaultLocale(selLangIdx == 0);

switch(m_ReminderFrequencyCtrl->GetSelection()) {
case 0:
Expand Down
2 changes: 1 addition & 1 deletion html/inc/language_names.inc
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

$language_names = array(
array("ar", "العربية", "Arabic"),
array("az", "Азәрбајҹан дили", "Azerbaijani"),
array("az", "Azərbaycan", "Azerbaijani"),
array("be", "Беларускі", "Belarusian"),
array("bg", "Български", "Bulgarian"),
array("ca", "Català", "Catalan"),
Expand Down