Browse Source

Get user preferred languages on apple devices (#3875)

Co-authored-by: Bubio <bubio66@gmail.com>
pull/3877/head
Andrew James 4 years ago committed by GitHub
parent
commit
ab95a29697
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      CMakeLists.txt
  2. 79
      Source/platform/locale.cpp
  3. 4
      Source/platform/locale.hpp

6
CMakeLists.txt

@ -275,6 +275,12 @@ if(APPLE)
COMPONENT Runtime) COMPONENT Runtime)
endif() endif()
find_library(COREFOUNDATION_LIBRARY CoreFoundation)
if(COREFOUNDATION_LIBRARY)
target_link_libraries(libdevilutionx PUBLIC "${COREFOUNDATION_LIBRARY}")
target_compile_definitions(libdevilutionx PRIVATE USE_COREFOUNDATION)
endif()
set(MACOSX_BUNDLE_LONG_VERSION_STRING "Version ${PROJECT_VERSION}") set(MACOSX_BUNDLE_LONG_VERSION_STRING "Version ${PROJECT_VERSION}")
set(CPACK On) set(CPACK On)
endif() endif()

79
Source/platform/locale.cpp

@ -15,13 +15,42 @@
#include <windows.h> #include <windows.h>
#include <winnls.h> #include <winnls.h>
// clang-format on // clang-format on
#elif defined(__APPLE__) and defined(USE_COREFOUNDATION)
#include "utils/stdcompat/algorithm.hpp" #include <CoreFoundation/CoreFoundation.h>
#else #else
#include <locale> #include <locale>
#endif #endif
#include "utils/stdcompat/algorithm.hpp"
#include "utils/stdcompat/string_view.hpp"
namespace devilution { namespace devilution {
namespace {
std::string IetfToPosix(string_view langCode)
{
/*
* Handle special case for simplified/traditional Chinese. IETF/BCP-47 specifies that only the script should be
* used to discriminate languages when the region doesn't add additional value. For chinese scripts zh-Hans is
* preferred over zh-Hans-CN (but platforms may include the region identifier anyway). POSIX locales don't use
* script in the same way so we need to convert these back to lang_region.
*/
if (langCode.substr(0, 7) == "zh-Hans") {
return "zh_CN";
}
if (langCode.substr(0, 7) == "zh-Hant") {
return "zh_TW";
}
std::string posixLangCode { langCode };
// if a region is included in the locale do a simple transformation to the expected POSIX style.
std::replace(posixLangCode.begin(), posixLangCode.end(), '-', '_');
return posixLangCode;
}
} // namespace
std::vector<std::string> GetLocales() std::vector<std::string> GetLocales()
{ {
@ -77,27 +106,26 @@ std::vector<std::string> GetLocales()
#elif defined(_WIN32) #elif defined(_WIN32)
#if WINVER >= 0x0600 #if WINVER >= 0x0600
auto wideCharToUtf8 = [](PWSTR wideString) { auto wideCharToUtf8 = [](PWSTR wideString) {
char utf8Buffer[12] {}; // WideCharToMultiByte potentially leaves the buffer unterminated, default initialise here as a workaround
// We only handle 5 character locales (lang2-region2), so don't bother reading past that. This does leave the char utf8Buffer[16] {};
// resulting string unterminated but the buffer was zero initialised anyway.
WideCharToMultiByte(CP_UTF8, 0, wideString, 5, utf8Buffer, sizeof(utf8Buffer), nullptr, nullptr); // Fetching up to 10 characters to allow for script tags
WideCharToMultiByte(CP_UTF8, 0, wideString, 10, utf8Buffer, sizeof(utf8Buffer), nullptr, nullptr);
// GetUserDefaultLocaleName could return an ISO 639-2/T string (three letter language code) or even an
// arbitrary custom locale, however we only handle 639-1 (two letter language code) locale names when checking // Windows NLS functions return IETF/BCP-47 locale codes (or potentially arbitrary custom locales) but devX
// the fallback language. // uses posix format codes, return the expected format
// if a region is included in the locale do a simple transformation to the expected POSIX style. return std::move(IetfToPosix(utf8Buffer));
std::replace(utf8Buffer, utf8Buffer + sizeof(utf8Buffer), '-', '_');
return std::move(std::string(utf8Buffer));
}; };
WCHAR localeBuffer[LOCALE_NAME_MAX_LENGTH]; WCHAR localeBuffer[LOCALE_NAME_MAX_LENGTH];
if (GetUserDefaultLocaleName(localeBuffer, LOCALE_NAME_MAX_LENGTH) != 0) { if (GetUserDefaultLocaleName(localeBuffer, sizeof(localeBuffer)) != 0) {
// Found a user default locale convert from WIN32's default of UTF16 to UTF8 and add to the list // Found a user default locale convert from WIN32's default of UTF16 to UTF8 and add to the list
locales.push_back(wideCharToUtf8(localeBuffer)); locales.push_back(wideCharToUtf8(localeBuffer));
} }
ULONG numberOfLanguages; ULONG numberOfLanguages;
ULONG bufferSize = LOCALE_NAME_MAX_LENGTH; ULONG bufferSize = sizeof(localeBuffer);
GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &numberOfLanguages, localeBuffer, &bufferSize); GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &numberOfLanguages, localeBuffer, &bufferSize);
PWSTR bufferOffset = localeBuffer; PWSTR bufferOffset = localeBuffer;
@ -123,8 +151,27 @@ std::vector<std::string> GetLocales()
locales.push_back(std::move(locale)); locales.push_back(std::move(locale));
} }
#endif #endif
#elif defined(__APPLE__) and defined(USE_COREFOUNDATION)
// Get the user's language list (in order of preference)
CFArrayRef languages = CFLocaleCopyPreferredLanguages();
CFIndex numLanguages = CFArrayGetCount(languages);
for (CFIndex i = 0; i < numLanguages; i++) {
auto language = static_cast<CFStringRef>(CFArrayGetValueAtIndex(languages, i));
char buffer[16];
if (CFStringGetCString(language, buffer, sizeof(buffer), kCFStringEncodingUTF8)) {
// Convert to the posix format expected by callers
locales.push_back(IetfToPosix(buffer));
}
}
CFRelease(languages);
#else #else
locales.emplace_back(std::locale("").name().substr(0, 5)); std::string locale = std::locale("").name();
// strip off any encoding specifier, devX uses UTF8.
locales.emplace_back(locale.substr(0, locale.find('.')));
#endif #endif
return locales; return locales;
} }

4
Source/platform/locale.hpp

@ -5,6 +5,10 @@
namespace devilution { namespace devilution {
/**
* @brief Returns a list of preferred languages based on user/system configuration.
* @return 0 or more POSIX locale codes (language code with optional region identifier)
*/
std::vector<std::string> GetLocales(); std::vector<std::string> GetLocales();
} // namespace devilution } // namespace devilution

Loading…
Cancel
Save