mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-16 19:43:06 +01:00
Merge pull request #19023 from Gymnasiast/refactor/18945
Close #18945: Allow languages to fall back to more than just en-GB
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
- Feature: [#18744] Cheat to allow using a regular path as a queue path.
|
||||
- Improved: [#18749] Ability to have 4 active awards for more than one month in a row.
|
||||
- Improved: [#18826] [Plugin] Added all actions and their documentation to plugin API.
|
||||
- Improved: [#18945] Languages can now fall back to other languages than English.
|
||||
- Fix: [#18467] “Selected only” Object Selection filter is active in Track Designs Manager, and cannot be toggled.
|
||||
- Fix: [#18905] Ride Construction window theme is not applied correctly.
|
||||
- Fix: [#18911] Mini Golf station does not draw correctly from all angles.
|
||||
|
||||
@@ -23,32 +23,33 @@
|
||||
// clang-format off
|
||||
const language_descriptor LanguagesDescriptors[LANGUAGE_COUNT] =
|
||||
{
|
||||
{ "", "", "", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_UNDEFINED
|
||||
{ "ar-EG", "Arabic (experimental)", "Arabic (experimental)", FAMILY(&TTFFamilySansSerif), true }, // LANGUAGE_ARABIC
|
||||
{ "ca-ES", "Catalan", u8"Català", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_CATALAN
|
||||
{ "zh-CN", "Chinese (Simplified)", "Chinese (Simplified)", FAMILY(&TTFFamilyChineseSimplified), false }, // LANGUAGE_CHINESE_SIMPLIFIED
|
||||
{ "zh-TW", "Chinese (Traditional)", "Chinese (Traditional)", FAMILY(&TTFFamilyChineseTraditional), false }, // LANGUAGE_CHINESE_TRADITIONAL
|
||||
{ "cs-CZ", "Czech", u8"Čeština", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_CZECH
|
||||
{ "da-DK", "Danish", "Dansk", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_DANISH
|
||||
{ "de-DE", "German", "Deutsch", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_GERMAN
|
||||
{ "en-GB", "English (UK)", "English (UK)", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_ENGLISH_UK
|
||||
{ "en-US", "English (US)", "English (US)", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_ENGLISH_US
|
||||
{ "eo-ZZ", "Esperanto", "Esperanto", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_ESPERANTO
|
||||
{ "es-ES", "Spanish", u8"Español", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_SPANISH
|
||||
{ "fr-FR", "French", u8"Français", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_FRENCH
|
||||
{ "it-IT", "Italian", "Italiano", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_ITALIAN
|
||||
{ "ja-JP", "Japanese", "Japanese", FAMILY(&TTFFamilyJapanese), false }, // LANGUAGE_JAPANESE
|
||||
{ "ko-KR", "Korean", "Korean", FAMILY(&TTFFamilyKorean), false }, // LANGUAGE_KOREAN
|
||||
{ "hu-HU", "Hungarian", "Magyar", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_HUNGARIAN
|
||||
{ "nl-NL", "Dutch", "Nederlands", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_DUTCH
|
||||
{ "nb-NO", "Norwegian", "Norsk", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_NORWEGIAN
|
||||
{ "pl-PL", "Polish", "Polski", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_POLISH
|
||||
{ "pt-BR", "Portuguese (BR)", u8"Português (BR)", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_PORTUGUESE_BR
|
||||
{ "ru-RU", "Russian", u8"Русский", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_RUSSIAN
|
||||
{ "fi-FI", "Finnish", "Suomi", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_FINNISH
|
||||
{ "sv-SE", "Swedish", "Svenska", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_SWEDISH
|
||||
{ "tr-TR", "Turkish", "Türkçe", FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_TURKISH
|
||||
{ "vi-VN", "Vietnamese", "Vietnamese", FAMILY(&TTFFamilySansSerif), false }, // LANGUAGE_VIETNAMESE
|
||||
{ "", "", "", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_UNDEFINED
|
||||
{ "ar-EG", "Arabic (experimental)", "Arabic (experimental)", LANGUAGE_UNDEFINED, FAMILY(&TTFFamilySansSerif), true }, // LANGUAGE_ARABIC
|
||||
{ "ca-ES", "Catalan", u8"Català", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_CATALAN
|
||||
{ "zh-CN", "Chinese (Simplified)", "Chinese (Simplified)", LANGUAGE_UNDEFINED, FAMILY(&TTFFamilyChineseSimplified), false }, // LANGUAGE_CHINESE_SIMPLIFIED
|
||||
{ "zh-TW", "Chinese (Traditional)", "Chinese (Traditional)", LANGUAGE_UNDEFINED, FAMILY(&TTFFamilyChineseTraditional), false }, // LANGUAGE_CHINESE_TRADITIONAL
|
||||
{ "cs-CZ", "Czech", u8"Čeština", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_CZECH
|
||||
{ "da-DK", "Danish", "Dansk", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_DANISH
|
||||
{ "de-DE", "German", "Deutsch", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_GERMAN
|
||||
{ "en-GB", "English (UK)", "English (UK)", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_ENGLISH_UK
|
||||
{ "en-US", "English (US)", "English (US)", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_ENGLISH_US
|
||||
{ "eo-ZZ", "Esperanto", "Esperanto", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_ESPERANTO
|
||||
{ "es-ES", "Spanish", u8"Español", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_SPANISH
|
||||
{ "fr-FR", "French", u8"Français", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_FRENCH
|
||||
{ "fr-CA", "French (CA)", u8"Français (CA)", LANGUAGE_FRENCH, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_FRENCH_CA
|
||||
{ "it-IT", "Italian", "Italiano", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_ITALIAN
|
||||
{ "ja-JP", "Japanese", "Japanese", LANGUAGE_UNDEFINED, FAMILY(&TTFFamilyJapanese), false }, // LANGUAGE_JAPANESE
|
||||
{ "ko-KR", "Korean", "Korean", LANGUAGE_UNDEFINED, FAMILY(&TTFFamilyKorean), false }, // LANGUAGE_KOREAN
|
||||
{ "hu-HU", "Hungarian", "Magyar", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_HUNGARIAN
|
||||
{ "nl-NL", "Dutch", "Nederlands", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_DUTCH
|
||||
{ "nb-NO", "Norwegian", "Norsk", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_NORWEGIAN
|
||||
{ "pl-PL", "Polish", "Polski", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_POLISH
|
||||
{ "pt-BR", "Portuguese (BR)", u8"Português (BR)", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_PORTUGUESE_BR
|
||||
{ "ru-RU", "Russian", u8"Русский", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_RUSSIAN
|
||||
{ "fi-FI", "Finnish", "Suomi", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_FINNISH
|
||||
{ "sv-SE", "Swedish", "Svenska", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_SWEDISH
|
||||
{ "tr-TR", "Turkish", "Türkçe", LANGUAGE_UNDEFINED, FAMILY_OPENRCT2_SPRITE, false }, // LANGUAGE_TURKISH
|
||||
{ "vi-VN", "Vietnamese", "Vietnamese", LANGUAGE_UNDEFINED, FAMILY(&TTFFamilySansSerif), false }, // LANGUAGE_VIETNAMESE
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ enum
|
||||
LANGUAGE_ESPERANTO,
|
||||
LANGUAGE_SPANISH,
|
||||
LANGUAGE_FRENCH,
|
||||
LANGUAGE_FRENCH_CA,
|
||||
LANGUAGE_ITALIAN,
|
||||
LANGUAGE_JAPANESE,
|
||||
LANGUAGE_KOREAN,
|
||||
@@ -75,6 +76,7 @@ struct language_descriptor
|
||||
const char* locale;
|
||||
const utf8* english_name;
|
||||
const utf8* native_name;
|
||||
uint8_t fallback;
|
||||
#if !defined(NO_TTF)
|
||||
TTFontFamily const* font_family;
|
||||
#else
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "LanguagePack.h"
|
||||
|
||||
#include "../Context.h"
|
||||
#include "../common.h"
|
||||
#include "../core/FileStream.h"
|
||||
#include "../core/Memory.hpp"
|
||||
@@ -18,6 +19,7 @@
|
||||
#include "../core/StringReader.h"
|
||||
#include "Language.h"
|
||||
#include "Localisation.h"
|
||||
#include "LocalisationService.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
@@ -580,6 +582,12 @@ namespace LanguagePackFactory
|
||||
return languagePack;
|
||||
}
|
||||
|
||||
std::unique_ptr<ILanguagePack> FromLanguageId(uint16_t id)
|
||||
{
|
||||
auto path = OpenRCT2::GetContext()->GetLocalisationService().GetLanguagePath(id);
|
||||
return LanguagePack::FromFile(id, path.c_str());
|
||||
}
|
||||
|
||||
std::unique_ptr<ILanguagePack> FromText(uint16_t id, const utf8* text)
|
||||
{
|
||||
auto languagePack = LanguagePack::FromText(id, text);
|
||||
|
||||
@@ -33,5 +33,6 @@ struct ILanguagePack
|
||||
namespace LanguagePackFactory
|
||||
{
|
||||
std::unique_ptr<ILanguagePack> FromFile(uint16_t id, const utf8* path);
|
||||
std::unique_ptr<ILanguagePack> FromLanguageId(uint16_t id);
|
||||
std::unique_ptr<ILanguagePack> FromText(uint16_t id, const utf8* text);
|
||||
} // namespace LanguagePackFactory
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "../Context.h"
|
||||
#include "../PlatformEnvironment.h"
|
||||
#include "../core/Guard.hpp"
|
||||
#include "../core/Path.hpp"
|
||||
#include "../interface/Fonts.h"
|
||||
#include "../object/ObjectManager.h"
|
||||
@@ -40,10 +41,9 @@ LocalisationService::~LocalisationService() = default;
|
||||
|
||||
const char* LocalisationService::GetString(StringId id) const
|
||||
{
|
||||
const char* result = nullptr;
|
||||
if (id == STR_EMPTY)
|
||||
{
|
||||
result = "";
|
||||
return "";
|
||||
}
|
||||
else if (id >= BASE_OBJECT_STRING_ID && id < BASE_OBJECT_STRING_ID + MAX_OBJECT_CACHED_STRINGS)
|
||||
{
|
||||
@@ -53,24 +53,21 @@ const char* LocalisationService::GetString(StringId id) const
|
||||
return _objectStrings[index].c_str();
|
||||
}
|
||||
|
||||
result = "(unallocated string)";
|
||||
return "(unallocated string)";
|
||||
}
|
||||
else if (id != STR_NONE)
|
||||
{
|
||||
if (_languageCurrent != nullptr)
|
||||
for (const auto& language : _loadedLanguages)
|
||||
{
|
||||
result = _languageCurrent->GetString(id);
|
||||
}
|
||||
if (result == nullptr && _languageFallback != nullptr)
|
||||
{
|
||||
result = _languageFallback->GetString(id);
|
||||
}
|
||||
if (result == nullptr)
|
||||
{
|
||||
result = "(undefined string)";
|
||||
const auto result = language->GetString(id);
|
||||
if (result != nullptr)
|
||||
return result;
|
||||
}
|
||||
|
||||
return "(undefined string)";
|
||||
}
|
||||
return result;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string LocalisationService::GetLanguagePath(uint32_t languageId) const
|
||||
@@ -98,49 +95,71 @@ void LocalisationService::OpenLanguage(int32_t id)
|
||||
throw std::invalid_argument("id was undefined");
|
||||
}
|
||||
|
||||
std::string filename;
|
||||
if (id != LANGUAGE_ENGLISH_UK)
|
||||
{
|
||||
filename = GetLanguagePath(LANGUAGE_ENGLISH_UK);
|
||||
_languageFallback = LanguagePackFactory::FromFile(LANGUAGE_ENGLISH_UK, filename.c_str());
|
||||
}
|
||||
|
||||
filename = GetLanguagePath(id);
|
||||
_languageCurrent = LanguagePackFactory::FromFile(id, filename.c_str());
|
||||
if (_languageCurrent != nullptr)
|
||||
auto preferredLanguage = LanguagePackFactory::FromLanguageId(id);
|
||||
if (preferredLanguage != nullptr)
|
||||
{
|
||||
_currentLanguage = id;
|
||||
_languageOrder.emplace_back(id);
|
||||
_loadedLanguages.emplace_back(std::move(preferredLanguage));
|
||||
TryLoadFonts(*this);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unable to open language " + std::to_string(id));
|
||||
}
|
||||
|
||||
auto checkLanguage = LanguagesDescriptors[id].fallback;
|
||||
while (checkLanguage != LANGUAGE_UNDEFINED)
|
||||
{
|
||||
_languageOrder.emplace_back(checkLanguage);
|
||||
auto fallbackLanguagePack = LanguagePackFactory::FromLanguageId(checkLanguage);
|
||||
if (fallbackLanguagePack != nullptr)
|
||||
{
|
||||
_loadedLanguages.emplace_back(std::move(fallbackLanguagePack));
|
||||
}
|
||||
|
||||
checkLanguage = LanguagesDescriptors[checkLanguage].fallback;
|
||||
}
|
||||
|
||||
if (id != LANGUAGE_ENGLISH_UK)
|
||||
{
|
||||
_languageOrder.emplace_back(LANGUAGE_ENGLISH_UK);
|
||||
auto englishLanguagePack = LanguagePackFactory::FromLanguageId(LANGUAGE_ENGLISH_UK);
|
||||
if (englishLanguagePack != nullptr)
|
||||
{
|
||||
_loadedLanguages.emplace_back(std::move(englishLanguagePack));
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Unable to open the English language file!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LocalisationService::CloseLanguages()
|
||||
{
|
||||
_languageFallback = nullptr;
|
||||
_languageCurrent = nullptr;
|
||||
_languageOrder.clear();
|
||||
_loadedLanguages.clear();
|
||||
_currentLanguage = LANGUAGE_UNDEFINED;
|
||||
}
|
||||
|
||||
std::tuple<StringId, StringId, StringId> 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);
|
||||
Guard::Assert(!_loadedLanguages.empty());
|
||||
auto result0 = _loadedLanguages[0]->GetScenarioOverrideStringId(scenarioFilename.c_str(), 0);
|
||||
auto result1 = _loadedLanguages[0]->GetScenarioOverrideStringId(scenarioFilename.c_str(), 1);
|
||||
auto result2 = _loadedLanguages[0]->GetScenarioOverrideStringId(scenarioFilename.c_str(), 2);
|
||||
return std::make_tuple(result0, result1, result2);
|
||||
}
|
||||
|
||||
StringId LocalisationService::GetObjectOverrideStringId(std::string_view legacyIdentifier, uint8_t index) const
|
||||
{
|
||||
if (_languageCurrent == nullptr)
|
||||
if (_loadedLanguages.empty())
|
||||
{
|
||||
return STR_NONE;
|
||||
}
|
||||
return _languageCurrent->GetObjectOverrideStringId(legacyIdentifier, index);
|
||||
return _loadedLanguages[0]->GetObjectOverrideStringId(legacyIdentifier, index);
|
||||
}
|
||||
|
||||
StringId LocalisationService::AllocateObjectString(const std::string& target)
|
||||
@@ -176,6 +195,11 @@ void LocalisationService::FreeObjectString(StringId stringId)
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<int32_t>& LocalisationService::GetLanguageOrder() const
|
||||
{
|
||||
return _languageOrder;
|
||||
}
|
||||
|
||||
int32_t LocalisationService_GetCurrentLanguage()
|
||||
{
|
||||
const auto& localisationService = GetContext()->GetLocalisationService();
|
||||
|
||||
@@ -34,8 +34,8 @@ namespace OpenRCT2::Localisation
|
||||
const std::shared_ptr<IPlatformEnvironment> _env;
|
||||
int32_t _currentLanguage{};
|
||||
bool _useTrueTypeFont{};
|
||||
std::unique_ptr<ILanguagePack> _languageFallback;
|
||||
std::unique_ptr<ILanguagePack> _languageCurrent;
|
||||
std::vector<int32_t> _languageOrder;
|
||||
std::vector<std::unique_ptr<ILanguagePack>> _loadedLanguages;
|
||||
std::stack<StringId> _availableObjectStringIds;
|
||||
std::vector<std::string> _objectStrings;
|
||||
|
||||
@@ -66,6 +66,7 @@ namespace OpenRCT2::Localisation
|
||||
void CloseLanguages();
|
||||
StringId AllocateObjectString(const std::string& target);
|
||||
void FreeObjectString(StringId stringId);
|
||||
const std::vector<int32_t>& GetLanguageOrder() const;
|
||||
};
|
||||
} // namespace OpenRCT2::Localisation
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "StringTable.h"
|
||||
|
||||
#include "../Context.h"
|
||||
#include "../core/IStream.hpp"
|
||||
#include "../core/Json.hpp"
|
||||
#include "../core/String.hpp"
|
||||
@@ -154,8 +155,8 @@ void StringTable::SetString(ObjectStringID id, uint8_t language, const std::stri
|
||||
|
||||
void StringTable::Sort()
|
||||
{
|
||||
auto targetLanguage = LocalisationService_GetCurrentLanguage();
|
||||
std::sort(_strings.begin(), _strings.end(), [targetLanguage](const StringTableEntry& a, const StringTableEntry& b) -> bool {
|
||||
const auto& languageOrder = OpenRCT2::GetContext()->GetLocalisationService().GetLanguageOrder();
|
||||
std::sort(_strings.begin(), _strings.end(), [languageOrder](const StringTableEntry& a, const StringTableEntry& b) -> bool {
|
||||
if (a.Id == b.Id)
|
||||
{
|
||||
if (a.LanguageId == b.LanguageId)
|
||||
@@ -163,22 +164,16 @@ void StringTable::Sort()
|
||||
return String::Compare(a.Text, b.Text, true) < 0;
|
||||
}
|
||||
|
||||
if (a.LanguageId == targetLanguage)
|
||||
for (const auto& language : languageOrder)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (b.LanguageId == targetLanguage)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a.LanguageId == LANGUAGE_ENGLISH_UK)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (b.LanguageId == LANGUAGE_ENGLISH_UK)
|
||||
{
|
||||
return false;
|
||||
if (a.LanguageId == language)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (b.LanguageId == language)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return a.LanguageId < b.LanguageId;
|
||||
|
||||
@@ -609,6 +609,7 @@ namespace Platform
|
||||
{ L"eo", LANGUAGE_ESPERANTO },
|
||||
{ L"es", LANGUAGE_SPANISH },
|
||||
{ L"fr", LANGUAGE_FRENCH },
|
||||
{ L"fr-CA", LANGUAGE_FRENCH_CA },
|
||||
{ L"it", LANGUAGE_ITALIAN },
|
||||
{ L"ja", LANGUAGE_JAPANESE },
|
||||
{ L"ko", LANGUAGE_KOREAN },
|
||||
|
||||
Reference in New Issue
Block a user