[wip] Create new localisation service

This commit is contained in:
Ted John 2018-04-24 13:18:05 +01:00
parent 090c444353
commit 8681b4fac9
6 changed files with 273 additions and 115 deletions

View File

@ -64,7 +64,7 @@
#include "interface/Viewport.h"
#include "Intro.h"
#include "localisation/Date.h"
#include "localisation/Language.h"
#include "localisation/LocalisationService.h"
#include "network/DiscordService.h"
#include "network/http.h"
#include "network/network.h"
@ -74,6 +74,7 @@
using namespace OpenRCT2;
using namespace OpenRCT2::Audio;
using namespace OpenRCT2::Localisation;
using namespace OpenRCT2::Ui;
namespace OpenRCT2
@ -87,6 +88,7 @@ namespace OpenRCT2
std::shared_ptr<IUiContext> const _uiContext;
// Services
std::shared_ptr<LocalisationService> _localisationService;
IObjectRepository * _objectRepository = nullptr;
IObjectManager * _objectManager = nullptr;
ITrackDesignRepository * _trackDesignRepository = nullptr;
@ -131,7 +133,6 @@ namespace OpenRCT2
{
window_close_all();
http_dispose();
language_close_all();
object_manager_unload_all_objects();
gfx_object_check_all_images_freed();
gfx_unload_g2();
@ -169,6 +170,11 @@ namespace OpenRCT2
return _env;
}
std::shared_ptr<LocalisationService> GetLocalisationService() override
{
return _localisationService;
}
IObjectManager * GetObjectManager() override
{
return _objectManager;
@ -350,19 +356,27 @@ namespace OpenRCT2
_discordService = new DiscordService();
#endif
if (!language_open(gConfigGeneral.language))
try
{
log_error("Failed to open configured language...");
if (!language_open(LANGUAGE_ENGLISH_UK))
_localisationService->OpenLanguage(gConfigGeneral.language, *_objectManager);
}
catch (const std::exception& e)
{
log_error("Failed to open configured language: %s", e.what());
try
{
log_fatal("Failed to open fallback language...");
_localisationService->OpenLanguage(LANGUAGE_ENGLISH_UK, *_objectManager);
}
catch (const std::exception&)
{
log_fatal("Failed to open fallback language: %s", e.what());
return false;
}
}
if (platform_process_is_elevated())
{
std::string elevationWarning = language_get_string(STR_ADMIN_NOT_RECOMMENDED);
std::string elevationWarning = _localisationService->GetString(STR_ADMIN_NOT_RECOMMENDED);
if (gOpenRCT2Headless)
{
Console::Error::WriteLine(elevationWarning.c_str());

View File

@ -77,6 +77,11 @@ namespace OpenRCT2
interface IAudioContext;
}
namespace Localisation
{
class LocalisationService;
}
namespace Ui
{
interface IUiContext;
@ -92,6 +97,7 @@ namespace OpenRCT2
virtual std::shared_ptr<Audio::IAudioContext> GetAudioContext() abstract;
virtual std::shared_ptr<Ui::IUiContext> GetUiContext() abstract;
virtual std::shared_ptr<IPlatformEnvironment> GetPlatformEnvironment() abstract;
virtual std::shared_ptr<Localisation::LocalisationService> GetLocalisationService() abstract;
virtual IObjectManager * GetObjectManager() abstract;
virtual IObjectRepository * GetObjectRepository() abstract;
virtual ITrackDesignRepository * GetTrackDesignRepository() abstract;

View File

@ -22,10 +22,10 @@
#include "../interface/Fonts.h"
#include "../interface/FontFamilies.h"
#include "../object/ObjectManager.h"
#include "LanguagePack.h"
#include "../platform/platform.h"
#include "LanguagePack.h"
#include "Localisation.h"
#include "LocalisationService.h"
// clang-format off
const language_descriptor LanguagesDescriptors[LANGUAGE_COUNT] =
@ -55,12 +55,6 @@ const language_descriptor LanguagesDescriptors[LANGUAGE_COUNT] =
};
// clang-format on
sint32 gCurrentLanguage = LANGUAGE_UNDEFINED;
bool gUseTrueTypeFont = false;
static ILanguagePack * _languageFallback = nullptr;
static ILanguagePack * _languageCurrent = nullptr;
// clang-format off
const utf8 BlackUpArrowString[] = { (utf8)(uint8)0xC2, (utf8)(uint8)0x8E, (utf8)(uint8)0xE2, (utf8)(uint8)0x96, (utf8)(uint8)0xB2, (utf8)(uint8)0x00 };
const utf8 BlackDownArrowString[] = { (utf8)(uint8)0xC2, (utf8)(uint8)0x8E, (utf8)(uint8)0xE2, (utf8)(uint8)0x96, (utf8)(uint8)0xBC, (utf8)(uint8)0x00 };
@ -100,130 +94,53 @@ uint8 language_get_id_from_locale(const char * locale)
const char * language_get_string(rct_string_id id)
{
const char * result = nullptr;
if (id == STR_EMPTY)
{
result = "";
}
else if (id != STR_NONE)
{
if (_languageCurrent != nullptr)
{
result = _languageCurrent->GetString(id);
}
if (result == nullptr && _languageFallback != nullptr)
{
result = _languageFallback->GetString(id);
}
if (result == nullptr)
{
result = "(undefined string)";
}
}
return result;
}
static utf8 * GetLanguagePath(utf8 * buffer, size_t bufferSize, uint32 languageId)
{
const char * locale = LanguagesDescriptors[languageId].locale;
platform_get_openrct_data_path(buffer, bufferSize);
Path::Append(buffer, bufferSize, "language");
Path::Append(buffer, bufferSize, locale);
String::Append(buffer, bufferSize, ".txt");
return buffer;
const auto& localisationService = OpenRCT2::GetContext()->GetLocalisationService();
return localisationService->GetString(id);
}
bool language_open(sint32 id)
{
char filename[MAX_PATH];
language_close_all();
if (id == LANGUAGE_UNDEFINED)
auto context = OpenRCT2::GetContext();
const auto& localisationService = context->GetLocalisationService();
auto objectManager = context->GetObjectManager();
try
{
localisationService->OpenLanguage(id, *objectManager);
return true;
}
catch (const std::exception&)
{
return false;
}
if (id != LANGUAGE_ENGLISH_UK)
{
GetLanguagePath(filename, sizeof(filename), LANGUAGE_ENGLISH_UK);
_languageFallback = LanguagePackFactory::FromFile(LANGUAGE_ENGLISH_UK, filename);
}
GetLanguagePath(filename, sizeof(filename), id);
_languageCurrent = LanguagePackFactory::FromFile(id, filename);
if (_languageCurrent != nullptr)
{
gCurrentLanguage = id;
TryLoadFonts();
// Objects and their localised strings need to be refreshed
auto context = OpenRCT2::GetContext();
context->GetObjectManager()->ResetObjects();
return true;
}
return false;
}
void language_close_all()
{
SafeDelete(_languageFallback);
SafeDelete(_languageCurrent);
gCurrentLanguage = LANGUAGE_UNDEFINED;
}
constexpr rct_string_id NONSTEX_BASE_STRING_ID = 3463;
constexpr uint16 MAX_OBJECT_CACHED_STRINGS = 2048;
bool language_get_localised_scenario_strings(const utf8 *scenarioFilename, rct_string_id *outStringIds)
{
outStringIds[0] = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename, 0);
outStringIds[1] = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename, 1);
outStringIds[2] = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename, 2);
const auto& localisationService = OpenRCT2::GetContext()->GetLocalisationService();
auto result = localisationService->GetLocalisedScenarioStrings(scenarioFilename);
outStringIds[0] = std::get<0>(result);
outStringIds[1] = std::get<1>(result);
outStringIds[2] = std::get<2>(result);
return
outStringIds[0] != STR_NONE ||
outStringIds[1] != STR_NONE ||
outStringIds[2] != STR_NONE;
}
static bool _availableObjectStringIdsInitialised = false;
static std::stack<rct_string_id> _availableObjectStringIds;
void language_free_object_string(rct_string_id stringId)
{
if (stringId != 0)
{
if (_languageCurrent != nullptr)
{
_languageCurrent->RemoveString(stringId);
}
_availableObjectStringIds.push(stringId);
}
const auto& localisationService = OpenRCT2::GetContext()->GetLocalisationService();
localisationService->FreeObjectString(stringId);
}
rct_string_id language_get_object_override_string_id(const char * identifier, uint8 index)
{
if (_languageCurrent == nullptr)
{
return STR_NONE;
}
return _languageCurrent->GetObjectOverrideStringId(identifier, index);
const auto& localisationService = OpenRCT2::GetContext()->GetLocalisationService();
return localisationService->GetObjectOverrideStringId(identifier, index);
}
rct_string_id language_allocate_object_string(const std::string &target)
{
if (!_availableObjectStringIdsInitialised)
{
_availableObjectStringIdsInitialised = true;
for (rct_string_id stringId = NONSTEX_BASE_STRING_ID + MAX_OBJECT_CACHED_STRINGS; stringId >= NONSTEX_BASE_STRING_ID; stringId--)
{
_availableObjectStringIds.push(stringId);
}
}
rct_string_id stringId = _availableObjectStringIds.top();
_availableObjectStringIds.pop();
_languageCurrent->SetString(stringId, target);
return stringId;
const auto& localisationService = OpenRCT2::GetContext()->GetLocalisationService();
return localisationService->AllocateObjectString(target);
}

View File

@ -98,7 +98,6 @@ extern const utf8 CheckBoxMarkString[];
uint8 language_get_id_from_locale(const char * locale);
const char *language_get_string(rct_string_id id);
bool language_open(sint32 id);
void language_close_all();
uint32 utf8_get_next(const utf8 *char_ptr, const utf8 **nextchar_ptr);
utf8 *utf8_write_codepoint(utf8 *dst, uint32 codepoint);

View File

@ -0,0 +1,159 @@
#pragma region Copyright (c) 2018 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 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.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#include <stdexcept>
#include "../Context.h"
#include "../core/Path.hpp"
#include "../interface/Fonts.h"
#include "../object/ObjectManager.h"
#include "../PlatformEnvironment.h"
#include "Language.h"
#include "LanguagePack.h"
#include "LocalisationService.h"
#include "StringIds.h"
using namespace OpenRCT2;
using namespace OpenRCT2::Localisation;
static constexpr rct_string_id NONSTEX_BASE_STRING_ID = 3463;
static constexpr uint16 MAX_OBJECT_CACHED_STRINGS = 2048;
LocalisationService::LocalisationService(const std::shared_ptr<IPlatformEnvironment>& env)
: _env(env)
{
for (rct_string_id stringId = NONSTEX_BASE_STRING_ID + MAX_OBJECT_CACHED_STRINGS; stringId >= NONSTEX_BASE_STRING_ID; stringId--)
{
_availableObjectStringIds.push(stringId);
}
}
const char * LocalisationService::GetString(rct_string_id id) const
{
const char * result = nullptr;
if (id == STR_EMPTY)
{
result = "";
}
else if (id != STR_NONE)
{
if (_languageCurrent != nullptr)
{
result = _languageCurrent->GetString(id);
}
if (result == nullptr && _languageFallback != nullptr)
{
result = _languageFallback->GetString(id);
}
if (result == nullptr)
{
result = "(undefined string)";
}
}
return result;
}
std::string LocalisationService::GetLanguagePath(uint32 languageId) const
{
auto locale = std::string(LanguagesDescriptors[languageId].locale);
auto languageDirectory = _env->GetDirectoryPath(DIRBASE::OPENRCT2, DIRID::LANGUAGE);
auto languagePath = Path::Combine(languageDirectory, locale + "txt");
return languagePath;
}
void LocalisationService::OpenLanguage(sint32 id, IObjectManager& objectManager)
{
CloseLanguages();
if (id == LANGUAGE_UNDEFINED)
{
throw std::invalid_argument("id was undefined");
}
std::string filename;
if (id != LANGUAGE_ENGLISH_UK)
{
filename = GetLanguagePath(LANGUAGE_ENGLISH_UK);
_languageFallback = std::unique_ptr<ILanguagePack>(LanguagePackFactory::FromFile(LANGUAGE_ENGLISH_UK, filename.c_str()));
}
filename = GetLanguagePath(id);
_languageCurrent = std::unique_ptr<ILanguagePack>(LanguagePackFactory::FromFile(id, filename.c_str()));
if (_languageCurrent != nullptr)
{
_currentLanguage = id;
TryLoadFonts();
// Objects and their localised strings need to be refreshed
objectManager.ResetObjects();
}
throw std::runtime_error("Unable to open language " + std::to_string(id));
}
void LocalisationService::CloseLanguages()
{
_languageFallback = nullptr;
_languageCurrent = nullptr;
_currentLanguage = LANGUAGE_UNDEFINED;
}
std::tuple<rct_string_id, rct_string_id, rct_string_id> LocalisationService::GetLocalisedScenarioStrings(const std::string& scenarioFilename) const
{
auto result0 = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename.c_str(), 0);
auto result1 = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename.c_str(), 1);
auto result2 = _languageCurrent->GetScenarioOverrideStringId(scenarioFilename.c_str(), 2);
return std::make_tuple(result0, result1, result2);
}
rct_string_id LocalisationService::GetObjectOverrideStringId(const char * identifier, uint8 index) const
{
if (_languageCurrent == nullptr)
{
return STR_NONE;
}
return _languageCurrent->GetObjectOverrideStringId(identifier, index);
}
rct_string_id LocalisationService::AllocateObjectString(const std::string& target)
{
auto stringId = _availableObjectStringIds.top();
_availableObjectStringIds.pop();
_languageCurrent->SetString(stringId, target);
return stringId;
}
void LocalisationService::FreeObjectString(rct_string_id stringId)
{
if (stringId != STR_EMPTY)
{
if (_languageCurrent != nullptr)
{
_languageCurrent->RemoveString(stringId);
}
_availableObjectStringIds.push(stringId);
}
}
sint32 LocalisationService_GetCurrentLanguage()
{
const auto& localisationService = GetContext()->GetLocalisationService();
return localisationService->GetCurrentLanguage();
}
bool LocalisationService_UseTrueTypeFont()
{
const auto& localisationService = GetContext()->GetLocalisationService();
return localisationService->UseTrueTypeFont();
}

View File

@ -0,0 +1,63 @@
#pragma region Copyright (c) 2018 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 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.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#include <memory>
#include <stack>
#include <string>
#include <tuple>
#include "../common.h"
interface ILanguagePack;
interface IObjectManager;
namespace OpenRCT2
{
interface IPlatformEnvironment;
}
namespace OpenRCT2::Localisation
{
class LocalisationService
{
private:
const std::shared_ptr<IPlatformEnvironment> _env;
sint32 _currentLanguage{};
bool _useTrueTypeFont{};
std::unique_ptr<ILanguagePack> _languageFallback;
std::unique_ptr<ILanguagePack> _languageCurrent;
std::stack<rct_string_id> _availableObjectStringIds;
public:
sint32 GetCurrentLanguage() const { return _currentLanguage; }
sint32 UseTrueTypeFont() const { return _useTrueTypeFont; }
LocalisationService(const std::shared_ptr<IPlatformEnvironment>& env);
const char * GetString(rct_string_id id) const;
std::tuple<rct_string_id, rct_string_id, rct_string_id> GetLocalisedScenarioStrings(const std::string& scenarioFilename) const;
rct_string_id GetObjectOverrideStringId(const char * identifier, uint8 index) const;
std::string GetLanguagePath(uint32 languageId) const;
void OpenLanguage(sint32 id, IObjectManager& objectManager);
void CloseLanguages();
rct_string_id AllocateObjectString(const std::string& target);
void FreeObjectString(rct_string_id stringId);
};
}
// Legacy getters
sint32 LocalisationService_GetCurrentLanguage();
bool LocalisationService_UseTrueTypeFont();