226 lines
6.8 KiB
C++
226 lines
6.8 KiB
C++
|
/***
|
||
|
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||
|
|
||
|
File: Locale.cpp
|
||
|
Date: 2021-6-11
|
||
|
Author: Reece
|
||
|
***/
|
||
|
#include <RuntimeInternal.hpp>
|
||
|
#include "Locale.hpp"
|
||
|
|
||
|
#include <locale>
|
||
|
#include <codecvt>
|
||
|
#include <wchar.h>
|
||
|
|
||
|
namespace Aurora::Locale
|
||
|
{
|
||
|
// Note: [0] out of touch boomers deprecated this before going for a nappy. we do not have a replacement yet
|
||
|
// [1] the native win32 implementation appears to be more well optimized than MSVC/stl
|
||
|
static std::wstring_convert<std::codecvt_utf8<wchar_t>> gUtf8Conv;
|
||
|
|
||
|
AUKN_SYM AuString ConvertFromWChar(const wchar_t *in)
|
||
|
{
|
||
|
return ConvertFromWChar(in, wcslen(in));
|
||
|
}
|
||
|
|
||
|
AUKN_SYM AuString ConvertFromWChar(const wchar_t *in, AuMach length)
|
||
|
{
|
||
|
#if defined (VENDOR_GENERIC_MICROSOFT) || defined(VENDOR_CONSOLE_MICROSOFT)
|
||
|
AuString ret;
|
||
|
auto chars = WideCharToMultiByte(CP_UTF8, 0, in, length, NULL, 0, NULL, NULL);
|
||
|
|
||
|
if (!chars)
|
||
|
{
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
ret.resize(chars);
|
||
|
WideCharToMultiByte(CP_UTF8, 0, in, length, ret.data(), ret.size(), NULL, NULL);
|
||
|
return ret;
|
||
|
#else
|
||
|
return gUtf8Conv.to_bytes(std::wstring(in, wcslen(in)));
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
AUKN_SYM std::wstring ConvertFromUTF8(const AuString &in)
|
||
|
{
|
||
|
#if defined (VENDOR_GENERIC_MICROSOFT) || defined(VENDOR_CONSOLE_MICROSOFT)
|
||
|
std::wstring ret;
|
||
|
auto chars = MultiByteToWideChar(CP_UTF8, 0, in.c_str(), in.length(), NULL, 0);
|
||
|
|
||
|
if (!chars)
|
||
|
{
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
ret.resize(chars);
|
||
|
MultiByteToWideChar(CP_UTF8, 0, in.c_str(), in.length(), ret.data(), ret.size());
|
||
|
return ret;
|
||
|
#else
|
||
|
return gUtf8Conv.from_bytes(in);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static AuString gCountryCode;
|
||
|
static AuString gLanguageCode;
|
||
|
static AuString gCodeset;
|
||
|
|
||
|
#if defined(AURORA_PLATFORM_WIN32)
|
||
|
|
||
|
static void SetLanguageWin32()
|
||
|
{
|
||
|
int ret;
|
||
|
wchar_t name[LOCALE_NAME_MAX_LENGTH] = { 0 };
|
||
|
|
||
|
ret = LCIDToLocaleName(LOCALE_USER_DEFAULT, name, LOCALE_NAME_MAX_LENGTH, LOCALE_ALLOW_NEUTRAL_NAMES);
|
||
|
SysAssert(ret, "Couldn't acquire win32 locale information");
|
||
|
|
||
|
wchar_t language[LOCALE_NAME_MAX_LENGTH] = { 0 };
|
||
|
ret = GetLocaleInfoEx(name, LOCALE_SISO639LANGNAME, language, LOCALE_NAME_MAX_LENGTH);
|
||
|
SysAssert(ret, "Couldn't acquire win32 provided ISO 639 map of {}", ConvertFromWChar(name));
|
||
|
|
||
|
wchar_t country[LOCALE_NAME_MAX_LENGTH] = { 0 };
|
||
|
ret = GetLocaleInfoEx(name, LOCALE_SISO3166CTRYNAME, country, LOCALE_NAME_MAX_LENGTH);
|
||
|
SysAssert(ret, "Couldn't acquire win32 provided ISO 3166 map of {}", ConvertFromWChar(name));
|
||
|
|
||
|
gCountryCode = ConvertFromWChar(country);
|
||
|
gLanguageCode = ConvertFromWChar(language);
|
||
|
gCodeset = "UTF-8";
|
||
|
}
|
||
|
|
||
|
#elif defined(AURORA_PLATFORM_LINUX) || defined(AURORA_PLATFORM_BSD)
|
||
|
|
||
|
static AuHashMap<unsigned char, AuString> ParseLocaleString(const AuString &locale)
|
||
|
{
|
||
|
static auto isCharacterSplitter = [&](unsigned char ch) -> bool
|
||
|
{
|
||
|
static AuList<unsigned char> characterSplitters = { '.', '_', '@' };
|
||
|
for (auto const splitter : characterSplitters)
|
||
|
{
|
||
|
if (splitter == ch)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
AuHashMap<unsigned char, AuString> parseTable;
|
||
|
|
||
|
AuMach startingIndex = 0;
|
||
|
unsigned char startingCharacter = '!';
|
||
|
for (AuMach i = 0; i < locale.size(); i++)
|
||
|
{
|
||
|
unsigned char curCh = locale[i];
|
||
|
|
||
|
if (!(isCharacterSplitter(curCh)))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
parseTable.insert(std::make_pair(startingCharacter, locale.substr(startingIndex, i - startingIndex)));
|
||
|
startingIndex = i + 1;
|
||
|
startingCharacter = curCh;
|
||
|
}
|
||
|
|
||
|
parseTable.insert(std::make_pair(startingCharacter, locale.substr(startingIndex, locale.size() - startingIndex)));
|
||
|
|
||
|
return parseTable;
|
||
|
}
|
||
|
|
||
|
static void SetLanguageUnix()
|
||
|
{
|
||
|
#if 0
|
||
|
// this doesn't seem to work with libc++ lol?
|
||
|
auto locale = std::locale("").name();
|
||
|
#else
|
||
|
setlocale(LC_ALL, "");
|
||
|
AuString locale = setlocale(LC_ALL, NULL);
|
||
|
#endif
|
||
|
|
||
|
if (locale == "C")
|
||
|
{
|
||
|
LogWarn("Improperly configured UNIX environment.");
|
||
|
LogWarn("This localization detection code was written in 2020, please follow the `language[_territory][.codeset][@modifier]` convention for user/sys locales.");
|
||
|
LogWarn("'C' is not a language, country, or anything with which we can discern anything meaningful from. Fix your scuffed unix operating system and try again later...");
|
||
|
SysPanic("You fools");
|
||
|
}
|
||
|
|
||
|
auto parseTable = ParseLocaleString(locale);
|
||
|
|
||
|
AuString *lc;
|
||
|
if ((TryFind(parseTable, '!', lc)) && (lc->size()))
|
||
|
{
|
||
|
gLanguageCode = *lc;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LogWarn("Improperly configured UNIX environment.");
|
||
|
LogWarn("Couldn't discern language from localization string: {}", locale);
|
||
|
SysPanic("You fools");
|
||
|
}
|
||
|
|
||
|
AuString *cc;
|
||
|
if ((TryFind(parseTable, '_', cc)) && (cc->size()))
|
||
|
{
|
||
|
gCountryCode = *cc;
|
||
|
}
|
||
|
|
||
|
AuString *cs;
|
||
|
if ((TryFind(parseTable, '.', cs)) && (cs->size()))
|
||
|
{
|
||
|
gCodeset = *cs;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
gCodeset = "UTF-8"; //also technically not true, but most UNIX/Linux applications expect UTF8 byte stirngs or UTF-32 wchar_t strings. this assumption shouldn't break anything
|
||
|
}
|
||
|
}
|
||
|
#define AURORA_HAS_UNIXLOCALE
|
||
|
#endif
|
||
|
|
||
|
#if defined(AURORA_PLATFORM_WIN32) || defined(AURORA_PLATFORM_LINUX)
|
||
|
static void SetLanguageEnvBlock()
|
||
|
{
|
||
|
const char *language;
|
||
|
if (language = getenv("AURORA_ENV_LANGUAGE"))
|
||
|
{
|
||
|
gLanguageCode = language;
|
||
|
}
|
||
|
|
||
|
const char *countryCode;
|
||
|
if (countryCode = getenv("AURORA_ENV_COUNTRY"))
|
||
|
{
|
||
|
gCountryCode = countryCode;
|
||
|
}
|
||
|
|
||
|
const char *codeSet;
|
||
|
if (codeSet = getenv("AURORA_ENV_CODESET"))
|
||
|
{
|
||
|
gCodeset = codeSet;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#define AURORA_HAS_ENVBLOCK
|
||
|
#endif
|
||
|
|
||
|
void Init()
|
||
|
{
|
||
|
#if defined(AURORA_PLATFORM_WIN32)
|
||
|
SetLanguageWin32();
|
||
|
#elif defined(AURORA_HAS_UNIXLOCALE)
|
||
|
SetLanguageUnix();
|
||
|
#endif
|
||
|
|
||
|
#if defined(AURORA_HAS_ENVBLOCK)
|
||
|
SetLanguageEnvBlock();
|
||
|
#endif
|
||
|
|
||
|
LogDbg("Initialized localization information (language: {}, country: {}, codeset: {})", gLanguageCode, gCountryCode, gCodeset);
|
||
|
}
|
||
|
|
||
|
AUKN_SYM LocalizationInfo GetLocale()
|
||
|
{
|
||
|
return LocalizationInfo(gLanguageCode, gCountryCode, gCodeset);
|
||
|
}
|
||
|
}
|