Add language switcher to the settings UI, closes #506

dev-time-step
krzys-h 2016-01-24 16:03:24 +01:00
parent 6d8a5bab31
commit 40352be5cc
14 changed files with 181 additions and 141 deletions

View File

@ -87,6 +87,7 @@ set(BASE_SOURCES
common/event.cpp
common/image.cpp
common/key.cpp
common/language.cpp
common/logger.cpp
common/misc.cpp
common/pathman.cpp

View File

@ -245,7 +245,6 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
OPT_RUNSCENE,
OPT_SCENETEST,
OPT_LOGLEVEL,
OPT_LANGUAGE,
OPT_LANGDIR,
OPT_DATADIR,
OPT_SAVEDIR,
@ -262,7 +261,6 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
{ "runscene", required_argument, nullptr, OPT_RUNSCENE },
{ "scenetest", no_argument, nullptr, OPT_SCENETEST },
{ "loglevel", required_argument, nullptr, OPT_LOGLEVEL },
{ "language", required_argument, nullptr, OPT_LANGUAGE },
{ "langdir", required_argument, nullptr, OPT_LANGDIR },
{ "datadir", required_argument, nullptr, OPT_DATADIR },
{ "savedir", required_argument, nullptr, OPT_SAVEDIR },
@ -306,7 +304,6 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
GetLogger()->Message(" -runscene sceneNNN run given scene on start\n");
GetLogger()->Message(" -scenetest win every mission right after it's loaded\n");
GetLogger()->Message(" -loglevel level set log level to level (one of: trace, debug, info, warn, error, none)\n");
GetLogger()->Message(" -language lang set language (one of: en, de, fr, pl, ru)\n");
GetLogger()->Message(" -langdir path set custom language directory path\n");
GetLogger()->Message(" -datadir path set custom data directory path\n");
GetLogger()->Message(" -savedir path set custom save directory path (must be writable)\n");
@ -371,19 +368,6 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
GetLogger()->SetLogLevel(logLevel);
break;
}
case OPT_LANGUAGE:
{
Language language;
if (! ParseLanguage(optarg, language))
{
GetLogger()->Error("Invalid language: '%s'\n", optarg);
return PARSE_ARGS_FAIL;
}
GetLogger()->Info("Using language %s\n", optarg);
m_language = language;
break;
}
case OPT_DATADIR:
{
m_pathManager->SetDataPath(optarg);
@ -440,7 +424,6 @@ ParseArgsStatus CApplication::ParseArguments(int argc, char *argv[])
bool CApplication::Create()
{
std::string path;
bool defaultValues = false;
GetLogger()->Info("Creating CApplication\n");
@ -454,27 +437,10 @@ bool CApplication::Create()
if (!GetConfigFile().Init())
{
GetLogger()->Warn("Config not found. Default values will be used!\n");
defaultValues = true;
GetLogger()->Warn("Config could not be loaded. Default values will be used!\n");
}
if (GetConfigFile().GetStringProperty("Language", "Lang", path))
{
Language language;
if (ParseLanguage(path, language))
{
m_language = language;
GetLogger()->Info("Setting language '%s' from ini file\n", path.c_str());
}
else
{
GetLogger()->Error("Invalid language '%s' in ini file\n", path.c_str());
}
}
SetLanguage(m_language);
//Create the sound instance.
// Create the sound instance.
#ifdef OPENAL_SOUND
if (!m_headless)
{
@ -647,7 +613,7 @@ bool CApplication::Create()
m_eventQueue = MakeUnique<CEventQueue>();
// Create the robot application.
m_controller = MakeUnique<CController>(this, !defaultValues);
m_controller = MakeUnique<CController>();
if (m_runSceneCategory == LevelCategory::Max)
m_controller->StartApp();
@ -1599,76 +1565,26 @@ char CApplication::GetLanguageChar() const
return langChar;
}
bool CApplication::ParseLanguage(const std::string& str, Language& language)
{
if (str == "en")
{
language = LANGUAGE_ENGLISH;
return true;
}
else if (str == "de")
{
language = LANGUAGE_GERMAN;
return true;
}
else if (str == "fr")
{
language = LANGUAGE_FRENCH;
return true;
}
else if (str == "pl")
{
language = LANGUAGE_POLISH;
return true;
}
else if (str == "ru")
{
language = LANGUAGE_RUSSIAN;
return true;
}
return false;
}
void CApplication::SetLanguage(Language language)
{
m_language = language;
/* Gettext initialization */
std::string locale = "";
switch (m_language)
static char envLang[50] = { 0 };
if (envLang[0] == 0)
{
default:
case LANGUAGE_ENV:
locale = "";
break;
case LANGUAGE_ENGLISH:
locale = "en_US.utf8";
break;
case LANGUAGE_GERMAN:
locale = "de_DE.utf8";
break;
case LANGUAGE_FRENCH:
locale = "fr_FR.utf8";
break;
case LANGUAGE_POLISH:
locale = "pl_PL.utf8";
break;
case LANGUAGE_RUSSIAN:
locale = "ru_RU.utf8";
break;
// Get this only at the first call, since this code modifies it
const char* currentEnvLang = gl_locale_name(LC_MESSAGES, "LC_MESSAGES");
if (currentEnvLang != nullptr)
{
strcpy(envLang, currentEnvLang);
}
}
if (locale.empty())
if (language == LANGUAGE_ENV)
{
const char* envLang = gl_locale_name(LC_MESSAGES, "LC_MESSAGES");
if (envLang == nullptr)
if (envLang[0] == 0)
{
GetLogger()->Error("Failed to get language from environment, setting default language\n");
m_language = LANGUAGE_ENGLISH;
@ -1704,15 +1620,41 @@ void CApplication::SetLanguage(Language language)
}
}
}
else
std::string locale = "";
switch (m_language)
{
std::string langStr = "LANGUAGE=";
langStr += locale;
strcpy(m_languageLocale, langStr.c_str());
putenv(m_languageLocale);
GetLogger()->Trace("SetLanguage: Set LANGUAGE=%s in environment\n", locale.c_str());
default:
locale = "";
break;
case LANGUAGE_ENGLISH:
locale = "en_US.utf8";
break;
case LANGUAGE_GERMAN:
locale = "de_DE.utf8";
break;
case LANGUAGE_FRENCH:
locale = "fr_FR.utf8";
break;
case LANGUAGE_POLISH:
locale = "pl_PL.utf8";
break;
case LANGUAGE_RUSSIAN:
locale = "ru_RU.utf8";
break;
}
std::string langStr = "LANGUAGE=";
langStr += locale;
strcpy(m_languageLocale, langStr.c_str());
putenv(m_languageLocale);
GetLogger()->Trace("SetLanguage: Set LANGUAGE=%s in environment\n", locale.c_str());
char* defaultLocale = setlocale(LC_ALL, ""); // Load system locale
GetLogger()->Debug("Default system locale: %s\n", defaultLocale);
setlocale(LC_NUMERIC, "C"); // Force numeric locale to "C" (fixes decimal point problems)

View File

@ -290,7 +290,6 @@ public:
Language GetLanguage() const;
char GetLanguageChar() const;
void SetLanguage(Language language);
static bool ParseLanguage(const std::string& str, Language& language);
//@}
//! Management of sleep in main loop (lowers CPU usage)

View File

@ -26,25 +26,15 @@
CController::CController(CApplication* app, bool configLoaded)
: m_app(app)
CController::CController()
{
m_main = MakeUnique<CRobotMain>();
if (configLoaded)
m_main->LoadConfigFile();
else
m_main->CreateConfigFile();
}
CController::~CController()
{
}
CApplication* CController::GetApplication()
{
return m_app;
}
CRobotMain* CController::GetRobotMain()
{
return m_main.get();

View File

@ -40,11 +40,9 @@ struct Event;
class CController
{
public:
CController(CApplication* app, bool configLoaded = true);
CController();
~CController();
//! Return CApplication instance
CApplication* GetApplication();
//! Return CRobotMain instance
CRobotMain* GetRobotMain();

View File

@ -227,6 +227,7 @@ void InitializeEventTypeTexts()
EVENT_TYPE_TEXT[EVENT_INTERFACE_SHADOW_MAPPING] = "EVENT_INTERFACE_SHADOW_MAPPING";
EVENT_TYPE_TEXT[EVENT_INTERFACE_SHADOW_MAPPING_QUALITY] = "EVENT_INTERFACE_SHADOW_MAPPING_QUALITY";
EVENT_TYPE_TEXT[EVENT_INTERFACE_SHADOW_MAPPING_BUFFER] = "EVENT_INTERFACE_SHADOW_MAPPING_BUFFER";
EVENT_TYPE_TEXT[EVENT_INTERFACE_LANGUAGE] = "EVENT_INTERFACE_LANGUAGE";
EVENT_TYPE_TEXT[EVENT_INTERFACE_KINFO1] = "EVENT_INTERFACE_KINFO1";
EVENT_TYPE_TEXT[EVENT_INTERFACE_KINFO2] = "EVENT_INTERFACE_KINFO2";

View File

@ -255,6 +255,7 @@ enum EventType
EVENT_INTERFACE_SHADOW_MAPPING = 787,
EVENT_INTERFACE_SHADOW_MAPPING_QUALITY = 788,
EVENT_INTERFACE_SHADOW_MAPPING_BUFFER = 789,
EVENT_INTERFACE_LANGUAGE = 790,
EVENT_INTERFACE_KINFO1 = 500,
EVENT_INTERFACE_KINFO2 = 501,

54
src/common/language.cpp Normal file
View File

@ -0,0 +1,54 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://gnu.org/licenses
*/
#include "common/language.h"
#include <map>
const std::map<Language, std::string> LANGUAGE_MAP = {
{ LANGUAGE_ENGLISH, "en" },
{ LANGUAGE_GERMAN, "de" },
{ LANGUAGE_FRENCH, "fr" },
{ LANGUAGE_POLISH, "pl" },
{ LANGUAGE_RUSSIAN, "ru" }
};
bool ParseLanguage(const std::string& str, Language& language)
{
for (auto it = LANGUAGE_MAP.begin(); it != LANGUAGE_MAP.end(); ++it)
{
if (it->second == str)
{
language = it->first;
return true;
}
}
return false;
}
bool LanguageToString(const Language& language, std::string& str)
{
if (LANGUAGE_MAP.count(language) > 0)
{
str = LANGUAGE_MAP.at(language);
return true;
}
return false;
}

View File

@ -1,6 +1,6 @@
/*
* This file is part of the Colobot: Gold Edition source code
* Copyright (C) 2001-2015, Daniel Roux, EPSITEC SA & TerranovaTeam
* Copyright (C) 2001-2016, Daniel Roux, EPSITEC SA & TerranovaTeam
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
*
* This program is free software: you can redistribute it and/or modify
@ -19,6 +19,8 @@
#pragma once
#include <string>
/**
* \enum Language
* \brief Application language
@ -32,3 +34,6 @@ enum Language
LANGUAGE_POLISH = 3,
LANGUAGE_RUSSIAN = 4
};
bool ParseLanguage(const std::string& str, Language& language);
bool LanguageToString(const Language& language, std::string& str);

View File

@ -23,6 +23,7 @@
#include "app/input.h"
#include "common/config_file.h"
#include "common/logger.h"
#include "graphics/engine/camera.h"
#include "graphics/engine/engine.h"
@ -49,6 +50,8 @@ CSettings::CSettings()
m_IODim = Math::Point(320.0f/640.0f, (121.0f+18.0f*8)/480.0f);
m_IOPos.x = (1.0f-m_IODim.x)/2.0f; // in the middle
m_IOPos.y = (1.0f-m_IODim.y)/2.0f;
m_language = LANGUAGE_ENV;
}
void CSettings::SaveResolutionSettings(const Gfx::DeviceConfig& config)
@ -120,6 +123,9 @@ void CSettings::SaveSettings()
GetConfigFile().SetFloatProperty("Edit", "IODimX", m_IODim.x);
GetConfigFile().SetFloatProperty("Edit", "IODimY", m_IODim.y);
std::string lang = "";
LanguageToString(m_language, lang);
GetConfigFile().SetStringProperty("Language", "Lang", lang);
GetConfigFile().Save();
}
@ -135,6 +141,7 @@ void CSettings::LoadSettings()
int iValue = 0;
float fValue = 0.0f;
bool bValue = false;
std::string sValue = "";
GetConfigFile().GetBoolProperty("Setup", "Tooltips", m_tooltips);
GetConfigFile().GetBoolProperty("Setup", "InterfaceGlint", m_interfaceGlint);
@ -269,6 +276,17 @@ void CSettings::LoadSettings()
GetConfigFile().GetFloatProperty("Edit", "IOPosY", m_IOPos.y);
GetConfigFile().GetFloatProperty("Edit", "IODimX", m_IODim.x);
GetConfigFile().GetFloatProperty("Edit", "IODimY", m_IODim.y);
m_language = LANGUAGE_ENV;
if (GetConfigFile().GetStringProperty("Language", "Lang", sValue))
{
if (!sValue.empty() && !ParseLanguage(sValue, m_language))
{
GetLogger()->Error("Failed to parse language '%s' from config file. Default language will be used.\n",
sValue.c_str());
}
}
app->SetLanguage(m_language);
}
void CSettings::SetTooltips(bool tooltips)
@ -392,3 +410,14 @@ Math::Point CSettings::GetIODim()
{
return m_IODim;
}
void CSettings::SetLanguage(Language language)
{
m_language = language;
CApplication::GetInstancePointer()->SetLanguage(m_language);
}
Language CSettings::GetLanguage()
{
return m_language;
}

View File

@ -19,6 +19,7 @@
#pragma once
#include "common/language.h"
#include "common/singleton.h"
#include "math/point.h"
@ -80,6 +81,9 @@ public:
Math::Point GetIODim();
//@}
void SetLanguage(Language language);
Language GetLanguage();
protected:
bool m_tooltips;
bool m_interfaceGlint;
@ -94,4 +98,6 @@ protected:
bool m_IOPublic;
Math::Point m_IOPos;
Math::Point m_IODim;
Language m_language;
};

View File

@ -262,6 +262,10 @@ CRobotMain::CRobotMain()
m_engine->SetTracePrecision(1.0f);
m_settings->LoadSettings();
m_settings->SaveSettings();
m_settings->SaveResolutionSettings(m_app->GetVideoConfig());
SelectPlayer(CPlayerProfile::GetLastName());
CScriptFunctions::Init();
@ -321,21 +325,6 @@ void CRobotMain::ReloadAllTextures()
}
}
//! Creates the file colobot.ini at the first time
void CRobotMain::CreateConfigFile()
{
m_settings->SaveSettings();
m_settings->SaveResolutionSettings(m_app->GetVideoConfig());
GetConfigFile().Save();
}
void CRobotMain::LoadConfigFile()
{
m_settings->LoadSettings();
}
std::string PhaseToString(Phase phase)
{
if (phase == PHASE_WELCOME1) return "PHASE_WELCOME1";

View File

@ -160,9 +160,6 @@ public:
Ui::CDisplayText* GetDisplayText();
CPauseManager* GetPauseManager();
void CreateConfigFile();
void LoadConfigFile();
void ResetAfterVideoConfigChanged();
void ReloadAllTextures();

View File

@ -31,6 +31,7 @@
#include "ui/controls/check.h"
#include "ui/controls/interface.h"
#include "ui/controls/label.h"
#include "ui/controls/list.h"
#include "ui/controls/slider.h"
#include "ui/controls/window.h"
@ -52,6 +53,7 @@ void CScreenSetupGame::CreateInterface()
CLabel* pl;
CCheck* pc;
CSlider* psl;
CList* pli;
Math::Point pos, ddim;
std::string name;
@ -131,6 +133,19 @@ void CScreenSetupGame::CreateInterface()
pos.y -= 0.048f;
pc = pw->CreateCheck(pos, ddim, -1, EVENT_INTERFACE_EDITVALUE);
pc->SetState(STATE_SHADOW);
pos.y -= 0.048f;
ddim.y = dim.y*3.0f;
pos.y -= ddim.y;
pli = pw->CreateList(pos, ddim, 0, EVENT_INTERFACE_LANGUAGE);
pli->SetState(STATE_SHADOW);
// TODO: Add something like GetSupportedLanguages() and GetLanguageFriendlyName() for this
pli->SetItemName(1+LANGUAGE_ENV, "[System default]");
pli->SetItemName(1+LANGUAGE_ENGLISH, "English");
pli->SetItemName(1+LANGUAGE_FRENCH, "French");
pli->SetItemName(1+LANGUAGE_GERMAN, "German");
pli->SetItemName(1+LANGUAGE_POLISH, "Polish");
pli->SetItemName(1+LANGUAGE_RUSSIAN, "Russian");
UpdateSetupButtons();
}
@ -227,11 +242,8 @@ bool CScreenSetupGame::EventProcess(const Event &event)
break;
case EVENT_INTERFACE_AUTOSAVE_INTERVAL:
ChangeSetupButtons();
UpdateSetupButtons();
break;
case EVENT_INTERFACE_AUTOSAVE_SLOTS:
case EVENT_INTERFACE_LANGUAGE:
ChangeSetupButtons();
UpdateSetupButtons();
break;
@ -249,6 +261,7 @@ void CScreenSetupGame::UpdateSetupButtons()
CWindow* pw;
CCheck* pc;
CSlider* ps;
CList* pli;
pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
if ( pw == nullptr ) return;
@ -345,6 +358,12 @@ void CScreenSetupGame::UpdateSetupButtons()
ps->SetState(STATE_ENABLE, m_main->GetAutosave());
ps->SetVisibleValue(m_main->GetAutosaveSlots());
}
pli = static_cast<CList*>(pw->SearchControl(EVENT_INTERFACE_LANGUAGE));
if ( pli != nullptr )
{
pli->SetSelect(1+m_settings->GetLanguage());
}
}
// Updates the engine function of the buttons after the setup phase.
@ -353,6 +372,7 @@ void CScreenSetupGame::ChangeSetupButtons()
{
CWindow* pw;
CSlider* ps;
CList* pli;
float value;
pw = static_cast<CWindow*>(m_interface->SearchControl(EVENT_WINDOW5));
@ -371,6 +391,14 @@ void CScreenSetupGame::ChangeSetupButtons()
value = ps->GetVisibleValue();
m_main->SetAutosaveSlots(static_cast<int>(round(value)));
}
pli = static_cast<CList*>(pw->SearchControl(EVENT_INTERFACE_LANGUAGE));
if ( pli != nullptr )
{
m_settings->SetLanguage(static_cast<Language>(pli->GetSelect()-1));
// TODO: A really ugly way to apply the change immediately
m_main->ChangePhase(m_main->GetPhase());
}
}
} // namespace Ui