Add os_locale_str to get user locale

This function determines the preferred user locale setting.
This commit is contained in:
Robert Müller 2023-03-19 16:04:42 +01:00
parent 6e6dfc9659
commit 413227a5c1
3 changed files with 97 additions and 0 deletions

View file

@ -26,6 +26,7 @@
#if defined(CONF_FAMILY_UNIX)
#include <csignal>
#include <locale>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/utsname.h>
@ -51,6 +52,7 @@
#define _task_user_
#include <Carbon/Carbon.h>
#include <CoreFoundation/CoreFoundation.h>
#include <mach-o/dyld.h>
#include <mach/mach_time.h>
@ -4266,6 +4268,72 @@ int os_version_str(char *version, int length)
#endif
}
void os_locale_str(char *locale, size_t length)
{
#if defined(CONF_FAMILY_WINDOWS)
wchar_t wide_buffer[LOCALE_NAME_MAX_LENGTH];
dbg_assert(GetUserDefaultLocaleName(wide_buffer, std::size(wide_buffer)) > 0, "GetUserDefaultLocaleName failure");
// Assume maximum possible length for encoding as UTF-8.
char buffer[UTF8_BYTE_LENGTH * LOCALE_NAME_MAX_LENGTH + 1];
dbg_assert(WideCharToMultiByte(CP_UTF8, 0, wide_buffer, -1, buffer, sizeof(buffer), NULL, NULL) > 0, "WideCharToMultiByte failure");
str_copy(locale, buffer, length);
#elif defined(CONF_PLATFORM_MACOS)
CFLocaleRef locale_ref = CFLocaleCopyCurrent();
CFStringRef locale_identifier_ref = static_cast<CFStringRef>(CFLocaleGetValue(locale_ref, kCFLocaleIdentifier));
// Count number of UTF16 codepoints, +1 for zero-termination.
// Assume maximum possible length for encoding as UTF-8.
CFIndex locale_identifier_size = (UTF8_BYTE_LENGTH * CFStringGetLength(locale_identifier_ref) + 1) * sizeof(char);
char *locale_identifier = (char *)malloc(locale_identifier_size);
dbg_assert(CFStringGetCString(locale_identifier_ref, locale_identifier, locale_identifier_size, kCFStringEncodingUTF8), "CFStringGetCString failure");
str_copy(locale, locale_identifier, length);
free(locale_identifier);
CFRelease(locale_ref);
#else
static const char *ENV_VARIABLES[] = {
"LC_ALL",
"LC_MESSAGES",
"LANG",
};
locale[0] = '\0';
for(const char *env_variable : ENV_VARIABLES)
{
const char *env_value = getenv(env_variable);
if(env_value)
{
str_copy(locale, env_value, length);
break;
}
}
#endif
// Ensure RFC 3066 format:
// - use hyphens instead of underscores
// - truncate locale string after first non-standard letter
for(int i = 0; i < str_length(locale); ++i)
{
if(locale[i] == '_')
{
locale[i] = '-';
}
else if(locale[i] != '-' && !(locale[i] >= 'a' && locale[i] <= 'z') && !(locale[i] >= 'A' && locale[i] <= 'Z') && !(locale[i] >= '0' && locale[i] <= '9'))
{
locale[i] = '\0';
break;
}
}
// Use default if we could not determine the locale,
// i.e. if only the C or POSIX locale is available.
if(locale[0] == '\0' || str_comp(locale, "C") == 0 || str_comp(locale, "POSIX") == 0)
str_copy(locale, "en-US", length);
}
#if defined(CONF_EXCEPTION_HANDLING)
#if defined(CONF_FAMILY_WINDOWS)
static HMODULE exception_handling_module = nullptr;

View file

@ -2143,6 +2143,14 @@ char str_uppercase(char c);
int str_isallnum(const char *str);
unsigned str_quickhash(const char *str);
enum
{
/**
* The maximum bytes necessary to encode one Unicode codepoint with UTF-8.
*/
UTF8_BYTE_LENGTH = 4,
};
int str_utf8_to_skeleton(const char *str, int *buf, int buf_len);
/*
@ -2586,6 +2594,20 @@ int secure_rand_below(int below);
*/
int os_version_str(char *version, int length);
/**
* Returns a string of the preferred locale of the user / operating system.
* The string conforms to [RFC 3066](https://www.ietf.org/rfc/rfc3066.txt)
* and only contains the characters `a`-`z`, `A`-`Z`, `0`-`9` and `-`.
* If the preferred locale could not be determined this function
* falls back to the locale `"en-US"`.
*
* @param locale Buffer to use for the output.
* @param length Length of the output buffer.
*
* @remark The destination buffer will be zero-terminated.
*/
void os_locale_str(char *locale, size_t length);
#if defined(CONF_EXCEPTION_HANDLING)
void init_exception_handler();
void set_exception_handler_log_file(const char *log_file_path);

View file

@ -9,3 +9,10 @@ TEST(Os, VersionStr)
EXPECT_FALSE(os_version_str(aVersion, sizeof(aVersion)));
EXPECT_STRNE(aVersion, "");
}
TEST(Os, LocaleStr)
{
char aLocale[128];
os_locale_str(aLocale, sizeof(aLocale));
EXPECT_STRNE(aLocale, "");
}