diff --git a/src/openrct2/config/Config.cpp b/src/openrct2/config/Config.cpp index ce1a3c3ab6..774511bbc6 100644 --- a/src/openrct2/config/Config.cpp +++ b/src/openrct2/config/Config.cpp @@ -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("language", platform_get_locale_language(), Enum_LanguageEnum); + model->language = reader->GetEnum("language", Platform::GetLocaleLanguage(), Enum_LanguageEnum); model->measurement_format = reader->GetEnum( "measurement_format", platform_get_locale_measurement_format(), Enum_MeasurementFormat); model->play_intro = reader->GetBoolean("play_intro", false); diff --git a/src/openrct2/platform/Android.cpp b/src/openrct2/platform/Android.cpp index 69c12b624d..6f7124bf08 100644 --- a/src/openrct2/platform/Android.cpp +++ b/src/openrct2/platform/Android.cpp @@ -26,11 +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); diff --git a/src/openrct2/platform/Linux.cpp b/src/openrct2/platform/Linux.cpp index 6348328730..47b4d3fdd7 100644 --- a/src/openrct2/platform/Linux.cpp +++ b/src/openrct2/platform/Linux.cpp @@ -34,79 +34,6 @@ # include # include -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, ""); diff --git a/src/openrct2/platform/Platform.Android.cpp b/src/openrct2/platform/Platform.Android.cpp index 0036bc91a5..f332f43206 100644 --- a/src/openrct2/platform/Platform.Android.cpp +++ b/src/openrct2/platform/Platform.Android.cpp @@ -10,6 +10,7 @@ #ifdef __ANDROID__ # include "../core/Guard.hpp" +# include "../localisation/Language.h" # include "Platform2.h" namespace Platform @@ -54,6 +55,11 @@ namespace Platform { return false; } + + uint16_t GetLocaleLanguage() + { + return LANGUAGE_ENGLISH_UK; + } } // namespace Platform #endif diff --git a/src/openrct2/platform/Platform.Linux.cpp b/src/openrct2/platform/Platform.Linux.cpp index 762e5b3fdf..839c0d1104 100644 --- a/src/openrct2/platform/Platform.Linux.cpp +++ b/src/openrct2/platform/Platform.Linux.cpp @@ -9,6 +9,8 @@ #if defined(__unix__) && !defined(__ANDROID__) && !defined(__APPLE__) +# include +# include # include # include # include @@ -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,71 @@ 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; + } } // namespace Platform #endif diff --git a/src/openrct2/platform/Platform.Win32.cpp b/src/openrct2/platform/Platform.Win32.cpp index 92e2100222..ddd02a8400 100644 --- a/src/openrct2/platform/Platform.Win32.cpp +++ b/src/openrct2/platform/Platform.Win32.cpp @@ -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,75 @@ namespace Platform } return result; } + + uint16_t GetLocaleLanguage() + { + CHAR langCode[4]; + + if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVLANGNAME, reinterpret_cast(&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; + } } // namespace Platform #endif diff --git a/src/openrct2/platform/Platform.macOS.mm b/src/openrct2/platform/Platform.macOS.mm index fc19d67fe6..d82984d1a0 100644 --- a/src/openrct2/platform/Platform.macOS.mm +++ b/src/openrct2/platform/Platform.macOS.mm @@ -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,62 @@ 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* preferredLanguages = [NSLocale preferredLanguages]; + for (NSString* preferredLanguage in preferredLanguages) + { + uint16_t languageIdentifier; + if (HasMatchingLanguage(preferredLanguage, &languageIdentifier)) + { + return languageIdentifier; + } + } + + // Fallback + return LANGUAGE_ENGLISH_UK; + } + } } #endif diff --git a/src/openrct2/platform/Platform2.h b/src/openrct2/platform/Platform2.h index dee152a456..7c9c0b8721 100644 --- a/src/openrct2/platform/Platform2.h +++ b/src/openrct2/platform/Platform2.h @@ -41,8 +41,11 @@ 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(); 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); diff --git a/src/openrct2/platform/Windows.cpp b/src/openrct2/platform/Windows.cpp index 13aefacb2b..bf76477ee8 100644 --- a/src/openrct2/platform/Windows.cpp +++ b/src/openrct2/platform/Windows.cpp @@ -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(&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{}; diff --git a/src/openrct2/platform/macos.mm b/src/openrct2/platform/macos.mm index abfa15ee88..7ae60532d5 100644 --- a/src/openrct2/platform/macos.mm +++ b/src/openrct2/platform/macos.mm @@ -45,74 +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* 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 diff --git a/src/openrct2/platform/platform.h b/src/openrct2/platform/platform.h index 3b547ddbfb..14984bd440 100644 --- a/src/openrct2/platform/platform.h +++ b/src/openrct2/platform/platform.h @@ -107,7 +107,6 @@ bool platform_open_common_file_dialog(utf8* outFilename, file_dialog_desc* desc, 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();