Merge pull request #16397 from Gymnasiast/refactor/more-platform-functions

Convert locale language/currency functions to new framework
This commit is contained in:
Michael Steenbeek 2022-01-08 16:57:49 +01:00 committed by GitHub
commit 8d7413f934
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 265 additions and 278 deletions

View File

@ -151,7 +151,7 @@ namespace Config
model->autosave_amount = reader->GetInt32("autosave_amount", DEFAULT_NUM_AUTOSAVES_TO_KEEP);
model->confirmation_prompt = reader->GetBoolean("confirmation_prompt", false);
model->currency_format = reader->GetEnum<CurrencyType>(
"currency_format", platform_get_locale_currency(), Enum_Currency);
"currency_format", Platform::GetLocaleCurrency(), Enum_Currency);
model->custom_currency_rate = reader->GetInt32("custom_currency_rate", 10);
model->custom_currency_affix = reader->GetEnum<CurrencyAffix>(
"custom_currency_affix", CurrencyAffix::Suffix, Enum_CurrencySymbolAffix);
@ -164,7 +164,7 @@ namespace Config
model->rct1_path = reader->GetCString("rct1_path", nullptr);
model->rct2_path = reader->GetString("game_path", "");
model->landscape_smoothing = reader->GetBoolean("landscape_smoothing", true);
model->language = reader->GetEnum<int32_t>("language", platform_get_locale_language(), Enum_LanguageEnum);
model->language = reader->GetEnum<int32_t>("language", Platform::GetLocaleLanguage(), Enum_LanguageEnum);
model->measurement_format = reader->GetEnum<MeasurementFormat>(
"measurement_format", platform_get_locale_measurement_format(), Enum_MeasurementFormat);
model->play_intro = reader->GetBoolean("play_intro", false);

View File

@ -26,16 +26,6 @@ bool platform_get_font_path(TTFFontDescriptor* font, utf8* buffer, size_t size)
}
# endif
uint16_t platform_get_locale_language()
{
return LANGUAGE_ENGLISH_UK;
}
CurrencyType platform_get_locale_currency()
{
return platform_get_currency_value(NULL);
}
MeasurementFormat platform_get_locale_measurement_format()
{
return MeasurementFormat::Metric;

View File

@ -17,8 +17,6 @@
# include <sys/sysctl.h>
# endif
# define OPENRCT2_MAX_COMMAND_LENGTH (2 * MAX_PATH)
# include <cstring>
# include <ctype.h>
# include <dlfcn.h>
@ -36,93 +34,6 @@
# include <locale.h>
# include <pwd.h>
uint16_t platform_get_locale_language()
{
const char* langString = setlocale(LC_MESSAGES, "");
if (langString != nullptr)
{
// The locale has the following form:
// language[_territory[.codeset]][@modifier]
// (see https://www.gnu.org/software/libc/manual/html_node/Locale-Names.html)
// longest on my system is 29 with codeset and modifier, so 32 for the pattern should be more than enough
char pattern[32];
// strip the codeset and modifier part
int32_t length = strlen(langString);
{
for (int32_t i = 0; i < length; ++i)
{
if (langString[i] == '.' || langString[i] == '@')
{
length = i;
break;
}
}
} // end strip
std::memcpy(pattern, langString, length); // copy all until first '.' or '@'
pattern[length] = '\0';
// find _ if present
const char* strip = strchr(pattern, '_');
if (strip != nullptr)
{
// could also use '-', but '?' is more flexible. Maybe LanguagesDescriptors will change.
// pattern is now "language?territory"
pattern[strip - pattern] = '?';
}
// Iterate through all available languages
for (int32_t i = 1; i < LANGUAGE_COUNT; ++i)
{
if (!fnmatch(pattern, LanguagesDescriptors[i].locale, 0))
{
return i;
}
}
// special cases :(
if (!fnmatch(pattern, "en_CA", 0))
{
return LANGUAGE_ENGLISH_US;
}
if (!fnmatch(pattern, "zh_CN", 0))
{
return LANGUAGE_CHINESE_SIMPLIFIED;
}
if (!fnmatch(pattern, "zh_TW", 0))
{
return LANGUAGE_CHINESE_TRADITIONAL;
}
// no exact match found trying only language part
if (strip != nullptr)
{
pattern[strip - pattern] = '*';
pattern[strip - pattern + 1] = '\0'; // pattern is now "language*"
for (int32_t i = 1; i < LANGUAGE_COUNT; ++i)
{
if (!fnmatch(pattern, LanguagesDescriptors[i].locale, 0))
{
return i;
}
}
}
}
return LANGUAGE_ENGLISH_UK;
}
CurrencyType platform_get_locale_currency()
{
char* langstring = setlocale(LC_MONETARY, "");
if (langstring == nullptr)
{
return platform_get_currency_value(NULL);
}
struct lconv* lc = localeconv();
return platform_get_currency_value(lc->int_curr_symbol);
}
MeasurementFormat platform_get_locale_measurement_format()
{
// LC_MEASUREMENT is GNU specific.

View File

@ -10,6 +10,7 @@
#ifdef __ANDROID__
# include "../core/Guard.hpp"
# include "../localisation/Language.h"
# include "Platform2.h"
namespace Platform
@ -54,6 +55,16 @@ namespace Platform
{
return false;
}
uint16_t GetLocaleLanguage()
{
return LANGUAGE_ENGLISH_UK;
}
CurrencyType GetLocaleCurrency()
{
return Platform::GetCurrencyValue(NULL);
}
} // namespace Platform
#endif

View File

@ -9,6 +9,8 @@
#if defined(__unix__) && !defined(__ANDROID__) && !defined(__APPLE__)
# include <cstring>
# include <fnmatch.h>
# include <limits.h>
# include <pwd.h>
# include <vector>
@ -23,6 +25,7 @@
# endif // __linux__
# include "../OpenRCT2.h"
# include "../core/Path.hpp"
# include "../localisation/Language.h"
# include "Platform2.h"
# include "platform.h"
@ -168,6 +171,85 @@ namespace Platform
{
return false;
}
uint16_t GetLocaleLanguage()
{
const char* langString = setlocale(LC_MESSAGES, "");
if (langString != nullptr)
{
// The locale has the following form:
// language[_territory[.codeset]][@modifier]
// (see https://www.gnu.org/software/libc/manual/html_node/Locale-Names.html)
// longest on my system is 29 with codeset and modifier, so 32 for the pattern should be more than enough
char pattern[32];
// strip the codeset and modifier part
int32_t length = strlen(langString);
{
for (int32_t i = 0; i < length; ++i)
{
if (langString[i] == '.' || langString[i] == '@')
{
length = i;
break;
}
}
} // end strip
std::memcpy(pattern, langString, length); // copy all until first '.' or '@'
pattern[length] = '\0';
// find _ if present
const char* strip = strchr(pattern, '_');
if (strip != nullptr)
{
// could also use '-', but '?' is more flexible. Maybe LanguagesDescriptors will change.
// pattern is now "language?territory"
pattern[strip - pattern] = '?';
}
// Iterate through all available languages
for (int32_t i = 1; i < LANGUAGE_COUNT; ++i)
{
if (!fnmatch(pattern, LanguagesDescriptors[i].locale, 0))
{
return i;
}
}
// special case
if (fnmatch(pattern, "en_CA", 0) == 0)
{
return LANGUAGE_ENGLISH_US;
}
// no exact match found trying only language part
if (strip != nullptr)
{
pattern[strip - pattern] = '*';
pattern[strip - pattern + 1] = '\0'; // pattern is now "language*"
for (int32_t i = 1; i < LANGUAGE_COUNT; ++i)
{
if (!fnmatch(pattern, LanguagesDescriptors[i].locale, 0))
{
return i;
}
}
}
}
return LANGUAGE_ENGLISH_UK;
}
CurrencyType GetLocaleCurrency()
{
char* langstring = setlocale(LC_MONETARY, "");
if (langstring == nullptr)
{
return Platform::GetCurrencyValue(NULL);
}
struct lconv* lc = localeconv();
return Platform::GetCurrencyValue(lc->int_curr_symbol);
}
} // namespace Platform
#endif

View File

@ -34,6 +34,7 @@
# include "../common.h"
# include "../core/Path.hpp"
# include "../core/String.hpp"
# include "../localisation/Language.h"
# include "Platform2.h"
# include "platform.h"
@ -626,6 +627,86 @@ namespace Platform
}
return result;
}
uint16_t GetLocaleLanguage()
{
CHAR langCode[4];
if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, reinterpret_cast<LPSTR>(&langCode), sizeof(langCode))
== 0)
{
return LANGUAGE_UNDEFINED;
}
if (strcmp(langCode, "ENG") == 0)
{
return LANGUAGE_ENGLISH_UK;
}
if (strcmp(langCode, "ENU") == 0)
{
return LANGUAGE_ENGLISH_US;
}
if (strcmp(langCode, "DEU") == 0)
{
return LANGUAGE_GERMAN;
}
if (strcmp(langCode, "NLD") == 0)
{
return LANGUAGE_DUTCH;
}
if (strcmp(langCode, "FRA") == 0)
{
return LANGUAGE_FRENCH;
}
if (strcmp(langCode, "HUN") == 0)
{
return LANGUAGE_HUNGARIAN;
}
if (strcmp(langCode, "PLK") == 0)
{
return LANGUAGE_POLISH;
}
if (strcmp(langCode, "ESP") == 0)
{
return LANGUAGE_SPANISH;
}
if (strcmp(langCode, "SVE") == 0)
{
return LANGUAGE_SWEDISH;
}
if (strcmp(langCode, "ITA") == 0)
{
return LANGUAGE_ITALIAN;
}
if (strcmp(langCode, "POR") == 0)
{
return LANGUAGE_PORTUGUESE_BR;
}
if (strcmp(langCode, "FIN") == 0)
{
return LANGUAGE_FINNISH;
}
if (strcmp(langCode, "NOR") == 0)
{
return LANGUAGE_NORWEGIAN;
}
if (strcmp(langCode, "DAN") == 0)
{
return LANGUAGE_DANISH;
}
return LANGUAGE_UNDEFINED;
}
CurrencyType GetLocaleCurrency()
{
CHAR currCode[4];
if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SINTLSYMBOL, reinterpret_cast<LPSTR>(&currCode), sizeof(currCode)) == 0)
{
return Platform::GetCurrencyValue(nullptr);
}
return Platform::GetCurrencyValue(currCode);
}
} // namespace Platform
#endif

View File

@ -12,6 +12,7 @@
# include "../OpenRCT2.h"
# include "../core/Path.hpp"
# include "../core/String.hpp"
# include "../localisation/Language.h"
# include "Platform2.h"
// undefine `interface` and `abstract`, because it's causing conflicts with Objective-C's keywords
@ -150,6 +151,71 @@ namespace Platform
}
return false;
}
bool HasMatchingLanguage(NSString* preferredLocale, uint16_t* languageIdentifier)
{
@autoreleasepool
{
if ([preferredLocale isEqualToString:@"en"] || [preferredLocale isEqualToString:@"en-CA"])
{
*languageIdentifier = LANGUAGE_ENGLISH_US;
return YES;
}
// Find an exact match (language and region)
for (int i = 1; i < LANGUAGE_COUNT; i++)
{
if ([preferredLocale isEqualToString:[NSString stringWithUTF8String:LanguagesDescriptors[i].locale]])
{
*languageIdentifier = i;
return YES;
}
}
// Only check for a matching language
NSString* languageCode = [[preferredLocale componentsSeparatedByString:@"-"] firstObject];
for (int i = 1; i < LANGUAGE_COUNT; i++)
{
NSString* optionLanguageCode = [[[NSString stringWithUTF8String:LanguagesDescriptors[i].locale]
componentsSeparatedByString:@"-"] firstObject];
if ([languageCode isEqualToString:optionLanguageCode])
{
*languageIdentifier = i;
return YES;
}
}
return NO;
}
}
uint16_t GetLocaleLanguage()
{
@autoreleasepool
{
NSArray<NSString*>* preferredLanguages = [NSLocale preferredLanguages];
for (NSString* preferredLanguage in preferredLanguages)
{
uint16_t languageIdentifier;
if (HasMatchingLanguage(preferredLanguage, &languageIdentifier))
{
return languageIdentifier;
}
}
// Fallback
return LANGUAGE_ENGLISH_UK;
}
}
CurrencyType GetLocaleCurrency()
{
@autoreleasepool
{
NSString* currencyCode = [[NSLocale currentLocale] objectForKey:NSLocaleCurrencyCode];
return Platform::GetCurrencyValue(currencyCode.UTF8String);
}
}
}
#endif

View File

@ -41,8 +41,13 @@ namespace Platform
uint64_t GetLastModified(const std::string& path);
uint64_t GetFileSize(std::string_view path);
std::string ResolveCasing(const std::string& path, bool fileExists);
uint16_t GetLocaleLanguage();
CurrencyType GetLocaleCurrency();
CurrencyType GetCurrencyValue(const char* currCode);
rct2_time GetTimeLocal();
rct2_date GetDateLocal();
bool FindApp(const std::string& app, std::string* output);
int32_t Execute(const std::string& command, std::string* output = nullptr);

View File

@ -70,6 +70,24 @@ static LARGE_INTEGER _entryTimestamp;
namespace Platform
{
CurrencyType GetCurrencyValue(const char* currCode)
{
if (currCode == nullptr || strlen(currCode) < 3)
{
return CurrencyType::Pounds;
}
for (int32_t currency = 0; currency < EnumValue(CurrencyType::Count); ++currency)
{
if (strncmp(currCode, CurrencyDescriptors[currency].isoCode, 3) == 0)
{
return static_cast<CurrencyType>(currency);
}
}
return CurrencyType::Pounds;
}
rct2_date GetDateLocal()
{
auto time = std::time(nullptr);
@ -242,24 +260,6 @@ void platform_sleep(uint32_t ms)
#endif
}
CurrencyType platform_get_currency_value(const char* currCode)
{
if (currCode == nullptr || strlen(currCode) < 3)
{
return CurrencyType::Pounds;
}
for (int32_t currency = 0; currency < EnumValue(CurrencyType::Count); ++currency)
{
if (strncmp(currCode, CurrencyDescriptors[currency].isoCode, 3) == 0)
{
return static_cast<CurrencyType>(currency);
}
}
return CurrencyType::Pounds;
}
#ifndef __ANDROID__
float platform_get_default_scale()
{

View File

@ -185,74 +185,6 @@ std::string platform_get_rct2_steam_dir()
return "Rollercoaster Tycoon 2";
}
uint16_t platform_get_locale_language()
{
CHAR langCode[4];
if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, reinterpret_cast<LPSTR>(&langCode), sizeof(langCode)) == 0)
{
return LANGUAGE_UNDEFINED;
}
if (strcmp(langCode, "ENG") == 0)
{
return LANGUAGE_ENGLISH_UK;
}
if (strcmp(langCode, "ENU") == 0)
{
return LANGUAGE_ENGLISH_US;
}
if (strcmp(langCode, "DEU") == 0)
{
return LANGUAGE_GERMAN;
}
if (strcmp(langCode, "NLD") == 0)
{
return LANGUAGE_DUTCH;
}
if (strcmp(langCode, "FRA") == 0)
{
return LANGUAGE_FRENCH;
}
if (strcmp(langCode, "HUN") == 0)
{
return LANGUAGE_HUNGARIAN;
}
if (strcmp(langCode, "PLK") == 0)
{
return LANGUAGE_POLISH;
}
if (strcmp(langCode, "ESP") == 0)
{
return LANGUAGE_SPANISH;
}
if (strcmp(langCode, "SVE") == 0)
{
return LANGUAGE_SWEDISH;
}
if (strcmp(langCode, "ITA") == 0)
{
return LANGUAGE_ITALIAN;
}
if (strcmp(langCode, "POR") == 0)
{
return LANGUAGE_PORTUGUESE_BR;
}
if (strcmp(langCode, "FIN") == 0)
{
return LANGUAGE_FINNISH;
}
if (strcmp(langCode, "NOR") == 0)
{
return LANGUAGE_NORWEGIAN;
}
if (strcmp(langCode, "DAN") == 0)
{
return LANGUAGE_DANISH;
}
return LANGUAGE_UNDEFINED;
}
time_t platform_file_get_modified_time(const utf8* path)
{
WIN32_FILE_ATTRIBUTE_DATA data{};
@ -273,17 +205,6 @@ time_t platform_file_get_modified_time(const utf8* path)
return 0;
}
CurrencyType platform_get_locale_currency()
{
CHAR currCode[4];
if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SINTLSYMBOL, reinterpret_cast<LPSTR>(&currCode), sizeof(currCode)) == 0)
{
return platform_get_currency_value(nullptr);
}
return platform_get_currency_value(currCode);
}
MeasurementFormat platform_get_locale_measurement_format()
{
UINT measurement_system;

View File

@ -45,83 +45,6 @@ bool platform_get_font_path(TTFFontDescriptor* font, utf8* buffer, size_t size)
}
# endif // NO_TTF
bool platform_has_matching_language(NSString* preferredLocale, uint16_t* languageIdentifier)
{
@autoreleasepool
{
if ([preferredLocale isEqualToString:@"en"] || [preferredLocale isEqualToString:@"en-CA"])
{
*languageIdentifier = LANGUAGE_ENGLISH_US;
return YES;
}
if ([preferredLocale isEqualToString:@"zh-CN"])
{
*languageIdentifier = LANGUAGE_CHINESE_SIMPLIFIED;
return YES;
}
if ([preferredLocale isEqualToString:@"zh-TW"])
{
*languageIdentifier = LANGUAGE_CHINESE_TRADITIONAL;
return YES;
}
// Find an exact match (language and region)
for (int i = 1; i < LANGUAGE_COUNT; i++)
{
if ([preferredLocale isEqualToString:[NSString stringWithUTF8String:LanguagesDescriptors[i].locale]])
{
*languageIdentifier = i;
return YES;
}
}
// Only check for a matching language
NSString* languageCode = [[preferredLocale componentsSeparatedByString:@"-"] firstObject];
for (int i = 1; i < LANGUAGE_COUNT; i++)
{
NSString* optionLanguageCode = [[[NSString stringWithUTF8String:LanguagesDescriptors[i].locale]
componentsSeparatedByString:@"-"] firstObject];
if ([languageCode isEqualToString:optionLanguageCode])
{
*languageIdentifier = i;
return YES;
}
}
return NO;
}
}
uint16_t platform_get_locale_language()
{
@autoreleasepool
{
NSArray<NSString*>* preferredLanguages = [NSLocale preferredLanguages];
for (NSString* preferredLanguage in preferredLanguages)
{
uint16_t languageIdentifier;
if (platform_has_matching_language(preferredLanguage, &languageIdentifier))
{
return languageIdentifier;
}
}
// Fallback
return LANGUAGE_ENGLISH_UK;
}
}
CurrencyType platform_get_locale_currency()
{
@autoreleasepool
{
NSString* currencyCode = [[NSLocale currentLocale] objectForKey:NSLocaleCurrencyCode];
return platform_get_currency_value(currencyCode.UTF8String);
}
}
MeasurementFormat platform_get_locale_measurement_format()
{
@autoreleasepool

View File

@ -105,9 +105,6 @@ void platform_sleep(uint32_t ms);
void platform_get_user_directory(utf8* outPath, const utf8* subDirectory, size_t outSize);
bool platform_open_common_file_dialog(utf8* outFilename, file_dialog_desc* desc, size_t outSize);
utf8* platform_open_directory_browser(const utf8* title);
CurrencyType platform_get_locale_currency();
CurrencyType platform_get_currency_value(const char* currencyCode);
uint16_t platform_get_locale_language();
MeasurementFormat platform_get_locale_measurement_format();
TemperatureUnit platform_get_locale_temperature_format();
uint8_t platform_get_locale_date_format();