mirror of https://github.com/OpenRCT2/OpenRCT2.git
[wip] Create new localisation service
This commit is contained in:
parent
090c444353
commit
8681b4fac9
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
Loading…
Reference in New Issue