mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-15 08:53:07 +09:00
ICU: replace windows related source files with those from mypal68 (ICU 64.2)
This commit is contained in:
parent
1822f2031e
commit
3a65ef9ca8
10 changed files with 503 additions and 689 deletions
|
|
@ -32,18 +32,9 @@
|
|||
#include "cmemory.h"
|
||||
#include "unicode/uloc.h"
|
||||
|
||||
#if U_PLATFORM == U_PF_WINDOWS && defined(_MSC_VER) && (_MSC_VER >= 1500)
|
||||
/*
|
||||
* TODO: It seems like we should widen this to
|
||||
* either U_PLATFORM_USES_ONLY_WIN32_API (includes MinGW)
|
||||
* or U_PLATFORM_HAS_WIN32_API (includes MinGW and Cygwin)
|
||||
* but those use gcc and won't have defined(_MSC_VER).
|
||||
* We might need to #include some Windows header and test for some version macro from there.
|
||||
* Or call some Windows function and see what it returns.
|
||||
*/
|
||||
#define USE_WINDOWS_LCID_MAPPING_API
|
||||
#if U_PLATFORM_HAS_WIN32_API && UCONFIG_USE_WINDOWS_LCID_MAPPING_API
|
||||
#include <windows.h>
|
||||
#include <winnls.h>
|
||||
#include <winnls.h> // LCIDToLocaleName and LocaleNameToLCID
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
@ -973,7 +964,7 @@ idCmp(const char* id1, const char* id2)
|
|||
/**
|
||||
* Searches for a Windows LCID
|
||||
*
|
||||
* @param posixid the Posix style locale id.
|
||||
* @param posixID the Posix style locale id.
|
||||
* @param status gets set to U_ILLEGAL_ARGUMENT_ERROR when the Posix ID has
|
||||
* no equivalent Windows LCID.
|
||||
* @return the LCID
|
||||
|
|
@ -1035,7 +1026,7 @@ getPosixID(const ILcidPosixMap *this_0, uint32_t hostID)
|
|||
//
|
||||
/////////////////////////////////////
|
||||
*/
|
||||
#ifdef USE_WINDOWS_LCID_MAPPING_API
|
||||
#if U_PLATFORM_HAS_WIN32_API && UCONFIG_USE_WINDOWS_LCID_MAPPING_API
|
||||
/*
|
||||
* Various language tags needs to be changed:
|
||||
* quz -> qu
|
||||
|
|
@ -1053,6 +1044,7 @@ getPosixID(const ILcidPosixMap *this_0, uint32_t hostID)
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
U_CAPI int32_t
|
||||
uprv_convertToPosix(uint32_t hostid, char *posixID, int32_t posixIDCapacity, UErrorCode* status)
|
||||
{
|
||||
|
|
@ -1061,8 +1053,10 @@ uprv_convertToPosix(uint32_t hostid, char *posixID, int32_t posixIDCapacity, UEr
|
|||
UBool bLookup = TRUE;
|
||||
const char *pPosixID = NULL;
|
||||
|
||||
#ifdef USE_WINDOWS_LCID_MAPPING_API
|
||||
char locName[LOCALE_NAME_MAX_LENGTH] = {}; // ICU name can't be longer than Windows name
|
||||
#if U_PLATFORM_HAS_WIN32_API && UCONFIG_USE_WINDOWS_LCID_MAPPING_API
|
||||
static_assert(ULOC_FULLNAME_CAPACITY > LOCALE_NAME_MAX_LENGTH, "Windows locale names have smaller length than ICU locale names.");
|
||||
|
||||
char locName[157]; /* ULOC_FULLNAME_CAPACITY */
|
||||
|
||||
// Note: Windows primary lang ID 0x92 in LCID is used for Central Kurdish and
|
||||
// GetLocaleInfo() maps such LCID to "ku". However, CLDR uses "ku" for
|
||||
|
|
@ -1070,47 +1064,37 @@ uprv_convertToPosix(uint32_t hostid, char *posixID, int32_t posixIDCapacity, UEr
|
|||
// use the Windows API to resolve locale ID for this specific case.
|
||||
if ((hostid & 0x3FF) != 0x92) {
|
||||
int32_t tmpLen = 0;
|
||||
UChar windowsLocaleName[LOCALE_NAME_MAX_LENGTH]; // ULOC_FULLNAME_CAPACITY > LOCALE_NAME_MAX_LENGTH
|
||||
|
||||
char16_t windowsLocaleName[LOCALE_NAME_MAX_LENGTH] = {};
|
||||
#define LOCALE_SNAME 0x0000005c
|
||||
// Note: LOCALE_ALLOW_NEUTRAL_NAMES was enabled in Windows7+, prior versions did not handle neutral (no-region) locale names.
|
||||
tmpLen = LCIDToLocaleName(hostid, (PWSTR)windowsLocaleName, UPRV_LENGTHOF(windowsLocaleName), LOCALE_ALLOW_NEUTRAL_NAMES);
|
||||
tmpLen = GetLocaleInfoA(hostid, LOCALE_SNAME, (LPSTR)locName, UPRV_LENGTHOF(locName));
|
||||
if (tmpLen > 1) {
|
||||
int32_t i = 0;
|
||||
// Only need to look up in table if have _, eg for de-de_phoneb type alternate sort.
|
||||
bLookup = FALSE;
|
||||
for (i = 0; i < UPRV_LENGTHOF(locName); i++)
|
||||
{
|
||||
locName[i] = (char)(windowsLocaleName[i]);
|
||||
|
||||
// Windows locale name may contain sorting variant, such as "es-ES_tradnl".
|
||||
// In such cases, we need special mapping data found in the hardcoded table
|
||||
// in this source file.
|
||||
if (windowsLocaleName[i] == L'_')
|
||||
{
|
||||
// Keep the base locale, without variant
|
||||
// TODO: Should these be mapped from _phoneb to @collation=phonebook, etc.?
|
||||
locName[i] = '\0';
|
||||
tmpLen = i;
|
||||
bLookup = TRUE;
|
||||
break;
|
||||
}
|
||||
else if (windowsLocaleName[i] == L'-')
|
||||
{
|
||||
// Windows names use -, ICU uses _
|
||||
locName[i] = '_';
|
||||
}
|
||||
else if (windowsLocaleName[i] == L'\0')
|
||||
{
|
||||
// No point in doing more work than necessary
|
||||
break;
|
||||
}
|
||||
/* Windows locale name may contain sorting variant, such as "es-ES_tradnl".
|
||||
In such case, we need special mapping data found in the hardcoded table
|
||||
in this source file. */
|
||||
char *p = uprv_strchr(locName, '_');
|
||||
if (p) {
|
||||
/* Keep the base locale, without variant */
|
||||
*p = 0;
|
||||
tmpLen = uprv_strlen(locName);
|
||||
}
|
||||
// TODO: Need to understand this better, why isn't it an alias?
|
||||
else {
|
||||
/* No hardcoded table lookup necessary */
|
||||
bLookup = FALSE;
|
||||
}
|
||||
/* Change the tag separator from '-' to '_' */
|
||||
p = locName;
|
||||
while (*p) {
|
||||
if (*p == '-') {
|
||||
*p = '_';
|
||||
}
|
||||
p++;
|
||||
}
|
||||
FIX_LANGUAGE_ID_TAG(locName, tmpLen);
|
||||
pPosixID = locName;
|
||||
}
|
||||
}
|
||||
#endif // USE_WINDOWS_LCID_MAPPING_API
|
||||
#endif
|
||||
|
||||
if (bLookup) {
|
||||
const char *pCandidate = NULL;
|
||||
|
|
@ -1132,7 +1116,7 @@ uprv_convertToPosix(uint32_t hostid, char *posixID, int32_t posixIDCapacity, UEr
|
|||
}
|
||||
|
||||
if (pPosixID) {
|
||||
int32_t resLen = static_cast<int32_t>(uprv_strlen(pPosixID));
|
||||
int32_t resLen = uprv_strlen(pPosixID);
|
||||
int32_t copyLen = resLen <= posixIDCapacity ? resLen : posixIDCapacity;
|
||||
uprv_memcpy(posixID, pPosixID, copyLen);
|
||||
if (resLen < posixIDCapacity) {
|
||||
|
|
@ -1162,21 +1146,16 @@ uprv_convertToPosix(uint32_t hostid, char *posixID, int32_t posixIDCapacity, UEr
|
|||
//
|
||||
/////////////////////////////////////
|
||||
*/
|
||||
U_CAPI uint32_t
|
||||
uprv_convertToLCIDPlatform(const char* localeID)
|
||||
/*U_CAPI uint32_t
|
||||
uprv_convertToLCIDPlatform(const char* localeID, UErrorCode* status)
|
||||
{
|
||||
// The purpose of this function is to leverage native platform name->lcid
|
||||
if (U_FAILURE(*status)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The purpose of this function is to leverage the Windows platform name->lcid
|
||||
// conversion functionality when available.
|
||||
#ifdef USE_WINDOWS_LCID_MAPPING_API
|
||||
DWORD nameLCIDFlags = 0;
|
||||
UErrorCode myStatus = U_ZERO_ERROR;
|
||||
|
||||
// First check for a Windows name->LCID match, fall through to catch
|
||||
// ICU special cases, but Windows may know it already.
|
||||
#if LOCALE_ALLOW_NEUTRAL_NAMES
|
||||
nameLCIDFlags = LOCALE_ALLOW_NEUTRAL_NAMES;
|
||||
#endif /* LOCALE_ALLOW_NEUTRAL_NAMES */
|
||||
|
||||
#if U_PLATFORM_HAS_WIN32_API && UCONFIG_USE_WINDOWS_LCID_MAPPING_API
|
||||
int32_t len;
|
||||
char collVal[ULOC_KEYWORDS_CAPACITY] = {};
|
||||
char baseName[ULOC_FULLNAME_CAPACITY] = {};
|
||||
|
|
@ -1185,8 +1164,8 @@ uprv_convertToLCIDPlatform(const char* localeID)
|
|||
// Check any for keywords.
|
||||
if (uprv_strchr(localeID, '@'))
|
||||
{
|
||||
len = uloc_getKeywordValue(localeID, "collation", collVal, UPRV_LENGTHOF(collVal) - 1, &myStatus);
|
||||
if (U_SUCCESS(myStatus) && len > 0)
|
||||
len = uloc_getKeywordValue(localeID, "collation", collVal, UPRV_LENGTHOF(collVal) - 1, status);
|
||||
if (U_SUCCESS(*status) && len > 0)
|
||||
{
|
||||
// If it contains the keyword collation, return 0 so that the LCID lookup table will be used.
|
||||
return 0;
|
||||
|
|
@ -1194,9 +1173,9 @@ uprv_convertToLCIDPlatform(const char* localeID)
|
|||
else
|
||||
{
|
||||
// If the locale ID contains keywords other than collation, just use the base name.
|
||||
len = uloc_getBaseName(localeID, baseName, UPRV_LENGTHOF(baseName) - 1, &myStatus);
|
||||
len = uloc_getBaseName(localeID, baseName, UPRV_LENGTHOF(baseName) - 1, status);
|
||||
|
||||
if (U_SUCCESS(myStatus) && len > 0)
|
||||
if (U_SUCCESS(*status) && len > 0)
|
||||
{
|
||||
baseName[len] = 0;
|
||||
mylocaleID = baseName;
|
||||
|
|
@ -1206,9 +1185,9 @@ uprv_convertToLCIDPlatform(const char* localeID)
|
|||
|
||||
char asciiBCP47Tag[LOCALE_NAME_MAX_LENGTH] = {};
|
||||
// this will change it from de_DE@collation=phonebook to de-DE-u-co-phonebk form
|
||||
(void)uloc_toLanguageTag(mylocaleID, asciiBCP47Tag, UPRV_LENGTHOF(asciiBCP47Tag), FALSE, &myStatus);
|
||||
(void)uloc_toLanguageTag(mylocaleID, asciiBCP47Tag, UPRV_LENGTHOF(asciiBCP47Tag), FALSE, status);
|
||||
|
||||
if (U_SUCCESS(myStatus))
|
||||
if (U_SUCCESS(*status))
|
||||
{
|
||||
// Need it to be UTF-16, not 8-bit
|
||||
wchar_t bcp47Tag[LOCALE_NAME_MAX_LENGTH] = {};
|
||||
|
|
@ -1230,7 +1209,7 @@ uprv_convertToLCIDPlatform(const char* localeID)
|
|||
{
|
||||
// Ensure it's null terminated
|
||||
bcp47Tag[i] = L'\0';
|
||||
LCID lcid = LocaleNameToLCID(bcp47Tag, nameLCIDFlags);
|
||||
LCID lcid = LocaleNameToLCID(bcp47Tag, LOCALE_ALLOW_NEUTRAL_NAMES);
|
||||
if (lcid > 0)
|
||||
{
|
||||
// Found LCID from windows, return that one, unless its completely ambiguous
|
||||
|
|
@ -1244,12 +1223,12 @@ uprv_convertToLCIDPlatform(const char* localeID)
|
|||
}
|
||||
}
|
||||
#else
|
||||
(void)localeID; // Suppress unused variable warning.
|
||||
#endif /* USE_WINDOWS_LCID_MAPPING_API */
|
||||
(void) localeID; // Suppress unused variable warning.
|
||||
#endif
|
||||
|
||||
// No found, or not implemented on platforms without native name->lcid conversion
|
||||
// Nothing found, or not implemented.
|
||||
return 0;
|
||||
}
|
||||
}*/
|
||||
|
||||
U_CAPI uint32_t
|
||||
uprv_convertToLCID(const char *langID, const char* posixID, UErrorCode* status)
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@
|
|||
U_CAPI int32_t uprv_convertToPosix(uint32_t hostid, char* posixID, int32_t posixIDCapacity, UErrorCode* status);
|
||||
|
||||
/* Don't call these functions directly. Use uloc_getLCID instead. */
|
||||
U_CAPI uint32_t uprv_convertToLCIDPlatform(const char *localeID); // Leverage platform conversion if possible
|
||||
U_CAPI uint32_t uprv_convertToLCID(const char *langID, const char* posixID, UErrorCode* status);
|
||||
//U_CAPI uint32_t uprv_convertToLCIDPlatform(const char* localeID, UErrorCode* status); // Leverage platform conversion if possible
|
||||
U_CAPI uint32_t uprv_convertToLCID(const char* langID, const char* posixID, UErrorCode* status);
|
||||
|
||||
#endif /* LOCMAP_H */
|
||||
|
||||
|
|
|
|||
|
|
@ -103,17 +103,6 @@
|
|||
# include <windows.h>
|
||||
# include "unicode/uloc.h"
|
||||
# include "wintz.h"
|
||||
#if U_PLATFORM_HAS_WINUWP_API
|
||||
typedef PVOID LPMSG; // TODO: figure out how to get rid of this typedef
|
||||
#include <Windows.Globalization.h>
|
||||
#include <windows.system.userprofile.h>
|
||||
#include <wrl/wrappers/corewrappers.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
using namespace ABI::Windows::Foundation;
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
#endif
|
||||
#elif U_PLATFORM == U_PF_OS400
|
||||
# include <float.h>
|
||||
# include <qusec.h> /* error code structure */
|
||||
|
|
@ -252,7 +241,6 @@ u_signBit(double d) {
|
|||
UDate fakeClock_t0 = 0; /** Time to start the clock from **/
|
||||
UDate fakeClock_dt = 0; /** Offset (fake time - real time) **/
|
||||
UBool fakeClock_set = FALSE; /** True if fake clock has spun up **/
|
||||
static UMutex fakeClockMutex = U_MUTEX_INTIALIZER;
|
||||
|
||||
static UDate getUTCtime_real() {
|
||||
struct timeval posixTime;
|
||||
|
|
@ -261,6 +249,7 @@ static UDate getUTCtime_real() {
|
|||
}
|
||||
|
||||
static UDate getUTCtime_fake() {
|
||||
static UMutex fakeClockMutex = U_MUTEX_INTIALIZER;
|
||||
umtx_lock(&fakeClockMutex);
|
||||
if(!fakeClock_set) {
|
||||
UDate real = getUTCtime_real();
|
||||
|
|
@ -997,7 +986,8 @@ static char* searchForTZFile(const char* path, DefaultTZInfo* tzInfo) {
|
|||
/* Check each entry in the directory. */
|
||||
while((dirEntry = readdir(dirp)) != NULL) {
|
||||
const char* dirName = dirEntry->d_name;
|
||||
if (uprv_strcmp(dirName, SKIP1) != 0 && uprv_strcmp(dirName, SKIP2) != 0) {
|
||||
if (uprv_strcmp(dirName, SKIP1) != 0 && uprv_strcmp(dirName, SKIP2) != 0
|
||||
&& uprv_strcmp(TZFILE_SKIP, dirName) != 0 && uprv_strcmp(TZFILE_SKIP2, dirName) != 0) {
|
||||
/* Create a newpath with the new entry to test each entry in the directory. */
|
||||
CharString newpath(curpath, status);
|
||||
newpath.append(dirName, -1, status);
|
||||
|
|
@ -1024,7 +1014,7 @@ static char* searchForTZFile(const char* path, DefaultTZInfo* tzInfo) {
|
|||
*/
|
||||
if (result != NULL)
|
||||
break;
|
||||
} else if (uprv_strcmp(TZFILE_SKIP, dirName) != 0 && uprv_strcmp(TZFILE_SKIP2, dirName) != 0) {
|
||||
} else {
|
||||
if(compareBinaryFiles(TZDEFAULT, newpath.data(), tzInfo)) {
|
||||
int32_t amountToSkip = sizeof(TZZONEINFO) - 1;
|
||||
if (amountToSkip > newpath.length()) {
|
||||
|
|
@ -1078,7 +1068,7 @@ uprv_tzname(int n)
|
|||
// the other code path returns a pointer to a heap location.
|
||||
// If we don't have a name already, then tzname wouldn't be any
|
||||
// better, so just fall back.
|
||||
return uprv_strdup("Etc/UTC");
|
||||
return uprv_strdup("");
|
||||
#endif // !U_TZNAME
|
||||
|
||||
#else
|
||||
|
|
@ -1317,9 +1307,9 @@ uprv_pathIsAbsolute(const char *path)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
/* Temporary backup setting of ICU_DATA_DIR_PREFIX_ENV_VAR
|
||||
until some client wrapper makefiles are updated */
|
||||
#if U_PLATFORM_IS_DARWIN_BASED && TARGET_IPHONE_SIMULATOR
|
||||
/* Backup setting of ICU_DATA_DIR_PREFIX_ENV_VAR
|
||||
(needed for some Darwin ICU build environments) */
|
||||
#if U_PLATFORM_IS_DARWIN_BASED && TARGET_OS_SIMULATOR
|
||||
# if !defined(ICU_DATA_DIR_PREFIX_ENV_VAR)
|
||||
# define ICU_DATA_DIR_PREFIX_ENV_VAR "IPHONE_SIMULATOR_ROOT"
|
||||
# endif
|
||||
|
|
@ -1430,12 +1420,7 @@ static void U_CALLCONV dataDirectoryInitFn() {
|
|||
|
||||
if(path==NULL) {
|
||||
/* It looks really bad, set it to something. */
|
||||
#if U_PLATFORM_HAS_WIN32_API
|
||||
// Windows UWP will require icudtl.dat file in same directory as icuuc.dll
|
||||
path = ".\\";
|
||||
#else
|
||||
path = "";
|
||||
#endif
|
||||
}
|
||||
|
||||
u_setDataDirectory(path);
|
||||
|
|
@ -1633,11 +1618,7 @@ The variant cannot have dots in it.
|
|||
The 'rightmost' variant (@xxx) wins.
|
||||
The leftmost codepage (.xxx) wins.
|
||||
*/
|
||||
char *correctedPOSIXLocale = 0;
|
||||
const char* posixID = uprv_getPOSIXIDForDefaultLocale();
|
||||
const char *p;
|
||||
const char *q;
|
||||
int32_t len;
|
||||
|
||||
/* Format: (no spaces)
|
||||
ll [ _CC ] [ . MM ] [ @ VV]
|
||||
|
|
@ -1645,38 +1626,29 @@ The leftmost codepage (.xxx) wins.
|
|||
l = lang, C = ctry, M = charmap, V = variant
|
||||
*/
|
||||
|
||||
if (gCorrectedPOSIXLocale != NULL) {
|
||||
if (gCorrectedPOSIXLocale != nullptr) {
|
||||
return gCorrectedPOSIXLocale;
|
||||
}
|
||||
|
||||
if ((p = uprv_strchr(posixID, '.')) != NULL) {
|
||||
/* assume new locale can't be larger than old one? */
|
||||
correctedPOSIXLocale = static_cast<char *>(uprv_malloc(uprv_strlen(posixID)+1));
|
||||
/* Exit on memory allocation error. */
|
||||
if (correctedPOSIXLocale == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
uprv_strncpy(correctedPOSIXLocale, posixID, p-posixID);
|
||||
correctedPOSIXLocale[p-posixID] = 0;
|
||||
// Copy the ID into owned memory.
|
||||
// Over-allocate in case we replace "@" with "__".
|
||||
char *correctedPOSIXLocale = static_cast<char *>(uprv_malloc(uprv_strlen(posixID) + 1 + 1));
|
||||
if (correctedPOSIXLocale == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
uprv_strcpy(correctedPOSIXLocale, posixID);
|
||||
|
||||
/* do not copy after the @ */
|
||||
if ((p = uprv_strchr(correctedPOSIXLocale, '@')) != NULL) {
|
||||
correctedPOSIXLocale[p-correctedPOSIXLocale] = 0;
|
||||
char *limit;
|
||||
if ((limit = uprv_strchr(correctedPOSIXLocale, '.')) != nullptr) {
|
||||
*limit = 0;
|
||||
if ((limit = uprv_strchr(correctedPOSIXLocale, '@')) != nullptr) {
|
||||
*limit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Note that we scan the *uncorrected* ID. */
|
||||
if ((p = uprv_strrchr(posixID, '@')) != NULL) {
|
||||
if (correctedPOSIXLocale == NULL) {
|
||||
/* new locale can be 1 char longer than old one if @ -> __ */
|
||||
correctedPOSIXLocale = static_cast<char *>(uprv_malloc(uprv_strlen(posixID)+2));
|
||||
/* Exit on memory allocation error. */
|
||||
if (correctedPOSIXLocale == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
uprv_strncpy(correctedPOSIXLocale, posixID, p-posixID);
|
||||
correctedPOSIXLocale[p-posixID] = 0;
|
||||
}
|
||||
const char *p;
|
||||
if ((p = uprv_strrchr(posixID, '@')) != nullptr) {
|
||||
p++;
|
||||
|
||||
/* Take care of any special cases here.. */
|
||||
|
|
@ -1685,16 +1657,17 @@ The leftmost codepage (.xxx) wins.
|
|||
/* Don't worry about no__NY. In practice, it won't appear. */
|
||||
}
|
||||
|
||||
if (uprv_strchr(correctedPOSIXLocale,'_') == NULL) {
|
||||
if (uprv_strchr(correctedPOSIXLocale,'_') == nullptr) {
|
||||
uprv_strcat(correctedPOSIXLocale, "__"); /* aa@b -> aa__b (note this can make the new locale 1 char longer) */
|
||||
}
|
||||
else {
|
||||
uprv_strcat(correctedPOSIXLocale, "_"); /* aa_CC@b -> aa_CC_b */
|
||||
}
|
||||
|
||||
if ((q = uprv_strchr(p, '.')) != NULL) {
|
||||
const char *q;
|
||||
if ((q = uprv_strchr(p, '.')) != nullptr) {
|
||||
/* How big will the resulting string be? */
|
||||
len = (int32_t)(uprv_strlen(correctedPOSIXLocale) + (q-p));
|
||||
int32_t len = (int32_t)(uprv_strlen(correctedPOSIXLocale) + (q-p));
|
||||
uprv_strncat(correctedPOSIXLocale, p, q-p);
|
||||
correctedPOSIXLocale[len] = 0;
|
||||
}
|
||||
|
|
@ -1710,28 +1683,15 @@ The leftmost codepage (.xxx) wins.
|
|||
*/
|
||||
}
|
||||
|
||||
/* Was a correction made? */
|
||||
if (correctedPOSIXLocale != NULL) {
|
||||
posixID = correctedPOSIXLocale;
|
||||
}
|
||||
else {
|
||||
/* copy it, just in case the original pointer goes away. See j2395 */
|
||||
correctedPOSIXLocale = (char *)uprv_malloc(uprv_strlen(posixID) + 1);
|
||||
/* Exit on memory allocation error. */
|
||||
if (correctedPOSIXLocale == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
posixID = uprv_strcpy(correctedPOSIXLocale, posixID);
|
||||
}
|
||||
|
||||
if (gCorrectedPOSIXLocale == NULL) {
|
||||
if (gCorrectedPOSIXLocale == nullptr) {
|
||||
gCorrectedPOSIXLocale = correctedPOSIXLocale;
|
||||
gCorrectedPOSIXLocaleHeapAllocated = true;
|
||||
ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup);
|
||||
correctedPOSIXLocale = NULL;
|
||||
correctedPOSIXLocale = nullptr;
|
||||
}
|
||||
posixID = gCorrectedPOSIXLocale;
|
||||
|
||||
if (correctedPOSIXLocale != NULL) { /* Was already set - clean up. */
|
||||
if (correctedPOSIXLocale != nullptr) { /* Was already set - clean up. */
|
||||
uprv_free(correctedPOSIXLocale);
|
||||
}
|
||||
|
||||
|
|
@ -1747,58 +1707,16 @@ The leftmost codepage (.xxx) wins.
|
|||
return gCorrectedPOSIXLocale;
|
||||
}
|
||||
|
||||
// No cached value, need to determine the current value
|
||||
static WCHAR windowsLocale[LOCALE_NAME_MAX_LENGTH] = {};
|
||||
int length = GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, windowsLocale, LOCALE_NAME_MAX_LENGTH);
|
||||
|
||||
// Now we should have a Windows locale name that needs converted to the POSIX style.
|
||||
if (length > 0) // If length is 0, then the GetLocaleInfoEx failed.
|
||||
{
|
||||
// First we need to go from UTF-16 to char (and also convert from _ to - while we're at it.)
|
||||
char modifiedWindowsLocale[LOCALE_NAME_MAX_LENGTH] = {};
|
||||
|
||||
int32_t i;
|
||||
for (i = 0; i < UPRV_LENGTHOF(modifiedWindowsLocale); i++)
|
||||
{
|
||||
if (windowsLocale[i] == '_')
|
||||
{
|
||||
modifiedWindowsLocale[i] = '-';
|
||||
}
|
||||
else
|
||||
{
|
||||
modifiedWindowsLocale[i] = static_cast<char>(windowsLocale[i]);
|
||||
}
|
||||
|
||||
if (modifiedWindowsLocale[i] == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= UPRV_LENGTHOF(modifiedWindowsLocale))
|
||||
{
|
||||
// Ran out of room, can't really happen, maybe we'll be lucky about a matching
|
||||
// locale when tags are dropped
|
||||
modifiedWindowsLocale[UPRV_LENGTHOF(modifiedWindowsLocale) - 1] = '\0';
|
||||
}
|
||||
|
||||
// Now normalize the resulting name
|
||||
correctedPOSIXLocale = static_cast<char *>(uprv_malloc(POSIX_LOCALE_CAPACITY + 1));
|
||||
/* TODO: Should we just exit on memory allocation failure? */
|
||||
if (correctedPOSIXLocale)
|
||||
{
|
||||
int32_t posixLen = uloc_canonicalize(modifiedWindowsLocale, correctedPOSIXLocale, POSIX_LOCALE_CAPACITY, &status);
|
||||
if (U_SUCCESS(status))
|
||||
{
|
||||
*(correctedPOSIXLocale + posixLen) = 0;
|
||||
gCorrectedPOSIXLocale = correctedPOSIXLocale;
|
||||
gCorrectedPOSIXLocaleHeapAllocated = true;
|
||||
ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup);
|
||||
}
|
||||
else
|
||||
{
|
||||
uprv_free(correctedPOSIXLocale);
|
||||
}
|
||||
LCID id = GetThreadLocale();
|
||||
correctedPOSIXLocale = static_cast<char *>(uprv_malloc(POSIX_LOCALE_CAPACITY + 1));
|
||||
if (correctedPOSIXLocale) {
|
||||
int32_t posixLen = uprv_convertToPosix(id, correctedPOSIXLocale, POSIX_LOCALE_CAPACITY, &status);
|
||||
if (U_SUCCESS(status)) {
|
||||
*(correctedPOSIXLocale + posixLen) = 0;
|
||||
gCorrectedPOSIXLocale = correctedPOSIXLocale;
|
||||
ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup);
|
||||
} else {
|
||||
uprv_free(correctedPOSIXLocale);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -457,8 +457,6 @@ NULL
|
|||
typedef struct CanonicalizationMap {
|
||||
const char *id; /* input ID */
|
||||
const char *canonicalID; /* canonicalized output ID */
|
||||
const char *keyword; /* keyword, or NULL if none */
|
||||
const char *value; /* keyword value, or NULL if kw==NULL */
|
||||
} CanonicalizationMap;
|
||||
|
||||
/**
|
||||
|
|
@ -466,64 +464,16 @@ typedef struct CanonicalizationMap {
|
|||
* different semantic kinds of transformations.
|
||||
*/
|
||||
static const CanonicalizationMap CANONICALIZE_MAP[] = {
|
||||
{ "", "en_US_POSIX", NULL, NULL }, /* .NET name */
|
||||
{ "c", "en_US_POSIX", NULL, NULL }, /* POSIX name */
|
||||
{ "posix", "en_US_POSIX", NULL, NULL }, /* POSIX name (alias of C) */
|
||||
{ "art_LOJBAN", "jbo", NULL, NULL }, /* registered name */
|
||||
{ "az_AZ_CYRL", "az_Cyrl_AZ", NULL, NULL }, /* .NET name */
|
||||
{ "az_AZ_LATN", "az_Latn_AZ", NULL, NULL }, /* .NET name */
|
||||
{ "ca_ES_PREEURO", "ca_ES", "currency", "ESP" },
|
||||
{ "de__PHONEBOOK", "de", "collation", "phonebook" }, /* Old ICU name */
|
||||
{ "de_AT_PREEURO", "de_AT", "currency", "ATS" },
|
||||
{ "de_DE_PREEURO", "de_DE", "currency", "DEM" },
|
||||
{ "de_LU_PREEURO", "de_LU", "currency", "LUF" },
|
||||
{ "el_GR_PREEURO", "el_GR", "currency", "GRD" },
|
||||
{ "en_BE_PREEURO", "en_BE", "currency", "BEF" },
|
||||
{ "en_IE_PREEURO", "en_IE", "currency", "IEP" },
|
||||
{ "es__TRADITIONAL", "es", "collation", "traditional" }, /* Old ICU name */
|
||||
{ "es_ES_PREEURO", "es_ES", "currency", "ESP" },
|
||||
{ "eu_ES_PREEURO", "eu_ES", "currency", "ESP" },
|
||||
{ "fi_FI_PREEURO", "fi_FI", "currency", "FIM" },
|
||||
{ "fr_BE_PREEURO", "fr_BE", "currency", "BEF" },
|
||||
{ "fr_FR_PREEURO", "fr_FR", "currency", "FRF" },
|
||||
{ "fr_LU_PREEURO", "fr_LU", "currency", "LUF" },
|
||||
{ "ga_IE_PREEURO", "ga_IE", "currency", "IEP" },
|
||||
{ "gl_ES_PREEURO", "gl_ES", "currency", "ESP" },
|
||||
{ "hi__DIRECT", "hi", "collation", "direct" }, /* Old ICU name */
|
||||
{ "it_IT_PREEURO", "it_IT", "currency", "ITL" },
|
||||
{ "ja_JP_TRADITIONAL", "ja_JP", "calendar", "japanese" }, /* Old ICU name */
|
||||
{ "nb_NO_NY", "nn_NO", NULL, NULL }, /* "markus said this was ok" :-) */
|
||||
{ "nl_BE_PREEURO", "nl_BE", "currency", "BEF" },
|
||||
{ "nl_NL_PREEURO", "nl_NL", "currency", "NLG" },
|
||||
{ "pt_PT_PREEURO", "pt_PT", "currency", "PTE" },
|
||||
{ "sr_SP_CYRL", "sr_Cyrl_RS", NULL, NULL }, /* .NET name */
|
||||
{ "sr_SP_LATN", "sr_Latn_RS", NULL, NULL }, /* .NET name */
|
||||
{ "sr_YU_CYRILLIC", "sr_Cyrl_RS", NULL, NULL }, /* Linux name */
|
||||
{ "th_TH_TRADITIONAL", "th_TH", "calendar", "buddhist" }, /* Old ICU name */
|
||||
{ "uz_UZ_CYRILLIC", "uz_Cyrl_UZ", NULL, NULL }, /* Linux name */
|
||||
{ "uz_UZ_CYRL", "uz_Cyrl_UZ", NULL, NULL }, /* .NET name */
|
||||
{ "uz_UZ_LATN", "uz_Latn_UZ", NULL, NULL }, /* .NET name */
|
||||
{ "zh_CHS", "zh_Hans", NULL, NULL }, /* .NET name */
|
||||
{ "zh_CHT", "zh_Hant", NULL, NULL }, /* .NET name */
|
||||
{ "zh_GAN", "gan", NULL, NULL }, /* registered name */
|
||||
{ "zh_GUOYU", "zh", NULL, NULL }, /* registered name */
|
||||
{ "zh_HAKKA", "hak", NULL, NULL }, /* registered name */
|
||||
{ "zh_MIN_NAN", "nan", NULL, NULL }, /* registered name */
|
||||
{ "zh_WUU", "wuu", NULL, NULL }, /* registered name */
|
||||
{ "zh_XIANG", "hsn", NULL, NULL }, /* registered name */
|
||||
{ "zh_YUE", "yue", NULL, NULL }, /* registered name */
|
||||
};
|
||||
|
||||
typedef struct VariantMap {
|
||||
const char *variant; /* input ID */
|
||||
const char *keyword; /* keyword, or NULL if none */
|
||||
const char *value; /* keyword value, or NULL if kw==NULL */
|
||||
} VariantMap;
|
||||
|
||||
static const VariantMap VARIANT_MAP[] = {
|
||||
{ "EURO", "currency", "EUR" },
|
||||
{ "PINYIN", "collation", "pinyin" }, /* Solaris variant */
|
||||
{ "STROKE", "collation", "stroke" } /* Solaris variant */
|
||||
{ "art_LOJBAN", "jbo" }, /* registered name */
|
||||
{ "hy__AREVELA", "hy" }, /* Registered IANA variant */
|
||||
{ "hy__AREVMDA", "hyw" }, /* Registered IANA variant */
|
||||
{ "zh_GAN", "gan" }, /* registered name */
|
||||
{ "zh_GUOYU", "zh" }, /* registered name */
|
||||
{ "zh_HAKKA", "hak" }, /* registered name */
|
||||
{ "zh_MIN_NAN", "nan" }, /* registered name */
|
||||
{ "zh_WUU", "wuu" }, /* registered name */
|
||||
{ "zh_XIANG", "hsn" }, /* registered name */
|
||||
{ "zh_YUE", "yue" }, /* registered name */
|
||||
};
|
||||
|
||||
/* ### BCP47 Conversion *******************************************/
|
||||
|
|
@ -643,20 +593,12 @@ compareKeywordStructs(const void * /*context*/, const void *left, const void *ri
|
|||
return uprv_strcmp(leftString, rightString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Both addKeyword and addValue must already be in canonical form.
|
||||
* Either both addKeyword and addValue are NULL, or neither is NULL.
|
||||
* If they are not NULL they must be zero terminated.
|
||||
* If addKeyword is not NULL is must have length small enough to fit in KeywordStruct.keyword.
|
||||
*/
|
||||
static int32_t
|
||||
_getKeywords(const char *localeID,
|
||||
char prev,
|
||||
char *keywords, int32_t keywordCapacity,
|
||||
char *values, int32_t valuesCapacity, int32_t *valLen,
|
||||
UBool valuesToo,
|
||||
const char* addKeyword,
|
||||
const char* addValue,
|
||||
UErrorCode *status)
|
||||
{
|
||||
KeywordStruct keywordList[ULOC_MAX_NO_KEYWORDS];
|
||||
|
|
@ -755,33 +697,6 @@ _getKeywords(const char *localeID,
|
|||
}
|
||||
} while(pos);
|
||||
|
||||
/* Handle addKeyword/addValue. */
|
||||
if (addKeyword != NULL) {
|
||||
UBool duplicate = FALSE;
|
||||
U_ASSERT(addValue != NULL);
|
||||
/* Search for duplicate; if found, do nothing. Explicit keyword
|
||||
overrides addKeyword. */
|
||||
for (j=0; j<numKeywords; ++j) {
|
||||
if (uprv_strcmp(keywordList[j].keyword, addKeyword) == 0) {
|
||||
duplicate = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!duplicate) {
|
||||
if (numKeywords == maxKeywords) {
|
||||
*status = U_INTERNAL_PROGRAM_ERROR;
|
||||
return 0;
|
||||
}
|
||||
uprv_strcpy(keywordList[numKeywords].keyword, addKeyword);
|
||||
keywordList[numKeywords].keywordLen = (int32_t)uprv_strlen(addKeyword);
|
||||
keywordList[numKeywords].valueStart = addValue;
|
||||
keywordList[numKeywords].valueLen = (int32_t)uprv_strlen(addValue);
|
||||
++numKeywords;
|
||||
}
|
||||
} else {
|
||||
U_ASSERT(addValue == NULL);
|
||||
}
|
||||
|
||||
/* now we have a list of keywords */
|
||||
/* we need to sort it */
|
||||
uprv_sortArray(keywordList, numKeywords, sizeof(KeywordStruct), compareKeywordStructs, NULL, FALSE, status);
|
||||
|
|
@ -839,7 +754,7 @@ locale_getKeywords(const char *localeID,
|
|||
UErrorCode *status) {
|
||||
return _getKeywords(localeID, prev, keywords, keywordCapacity,
|
||||
values, valuesCapacity, valLen, valuesToo,
|
||||
NULL, NULL, status);
|
||||
status);
|
||||
}
|
||||
|
||||
U_CAPI int32_t U_EXPORT2
|
||||
|
|
@ -1188,20 +1103,6 @@ uloc_setKeywordValue(const char* keywordName,
|
|||
*/
|
||||
#define _isTerminator(a) ((a==0)||(a=='.')||(a=='@'))
|
||||
|
||||
static char* _strnchr(const char* str, int32_t len, char c) {
|
||||
U_ASSERT(str != 0 && len >= 0);
|
||||
while (len-- != 0) {
|
||||
char d = *str;
|
||||
if (d == c) {
|
||||
return (char*) str;
|
||||
} else if (d == 0) {
|
||||
break;
|
||||
}
|
||||
++str;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup 'key' in the array 'list'. The array 'list' should contain
|
||||
* a NULL entry, followed by more entries, and a second NULL entry.
|
||||
|
|
@ -1279,6 +1180,16 @@ ulocimp_getLanguage(const char *localeID,
|
|||
int32_t offset;
|
||||
char lang[4]={ 0, 0, 0, 0 }; /* temporary buffer to hold language code for searching */
|
||||
|
||||
if (uprv_stricmp(localeID, "root") == 0) {
|
||||
localeID += 4;
|
||||
} else if (uprv_strnicmp(localeID, "und", 3) == 0 &&
|
||||
(localeID[3] == '\0' ||
|
||||
localeID[3] == '-' ||
|
||||
localeID[3] == '_' ||
|
||||
localeID[3] == '@')) {
|
||||
localeID += 3;
|
||||
}
|
||||
|
||||
/* if it starts with i- or x- then copy that prefix */
|
||||
if(_isIDPrefix(localeID)) {
|
||||
if(i<languageCapacity) {
|
||||
|
|
@ -1476,50 +1387,6 @@ _getVariant(const char *localeID,
|
|||
return _getVariantEx(localeID, prev, variant, variantCapacity, FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete ALL instances of a variant from the given list of one or
|
||||
* more variants. Example: "FOO_EURO_BAR_EURO" => "FOO_BAR".
|
||||
* @param variants the source string of one or more variants,
|
||||
* separated by '_'. This will be MODIFIED IN PLACE. Not zero
|
||||
* terminated; if it is, trailing zero will NOT be maintained.
|
||||
* @param variantsLen length of variants
|
||||
* @param toDelete variant to delete, without separators, e.g. "EURO"
|
||||
* or "PREEURO"; not zero terminated
|
||||
* @param toDeleteLen length of toDelete
|
||||
* @return number of characters deleted from variants
|
||||
*/
|
||||
static int32_t
|
||||
_deleteVariant(char* variants, int32_t variantsLen,
|
||||
const char* toDelete, int32_t toDeleteLen)
|
||||
{
|
||||
int32_t delta = 0; /* number of chars deleted */
|
||||
for (;;) {
|
||||
UBool flag = FALSE;
|
||||
if (variantsLen < toDeleteLen) {
|
||||
return delta;
|
||||
}
|
||||
if (uprv_strncmp(variants, toDelete, toDeleteLen) == 0 &&
|
||||
(variantsLen == toDeleteLen ||
|
||||
(flag=(variants[toDeleteLen] == '_')) != 0))
|
||||
{
|
||||
int32_t d = toDeleteLen + (flag?1:0);
|
||||
variantsLen -= d;
|
||||
delta += d;
|
||||
if (variantsLen > 0) {
|
||||
uprv_memmove(variants, variants+d, variantsLen);
|
||||
}
|
||||
} else {
|
||||
char* p = _strnchr(variants, variantsLen, '_');
|
||||
if (p == NULL) {
|
||||
return delta;
|
||||
}
|
||||
++p;
|
||||
variantsLen -= (int32_t)(p - variants);
|
||||
variants = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Keyword enumeration */
|
||||
|
||||
typedef struct UKeywordsContext {
|
||||
|
|
@ -1698,8 +1565,6 @@ _canonicalize(const char* localeID,
|
|||
const char* tmpLocaleID;
|
||||
const char* keywordAssign = NULL;
|
||||
const char* separatorIndicator = NULL;
|
||||
const char* addKeyword = NULL;
|
||||
const char* addValue = NULL;
|
||||
char* name;
|
||||
char* variant = NULL; /* pointer into name, or NULL */
|
||||
|
||||
|
|
@ -1738,7 +1603,7 @@ _canonicalize(const char* localeID,
|
|||
len = (int32_t)uprv_strlen(d);
|
||||
|
||||
if (name != NULL) {
|
||||
uprv_strncpy(name, d, len);
|
||||
uprv_memcpy(name, d, len);
|
||||
}
|
||||
} else if(_isIDSeparator(*tmpLocaleID)) {
|
||||
const char *scriptID;
|
||||
|
|
@ -1864,27 +1729,6 @@ _canonicalize(const char* localeID,
|
|||
}
|
||||
}
|
||||
|
||||
/* Handle generic variants first */
|
||||
if (variant) {
|
||||
for (j=0; j<UPRV_LENGTHOF(VARIANT_MAP); j++) {
|
||||
const char* variantToCompare = VARIANT_MAP[j].variant;
|
||||
int32_t n = (int32_t)uprv_strlen(variantToCompare);
|
||||
int32_t variantLen = _deleteVariant(variant, uprv_min(variantSize, (nameCapacity-len)), variantToCompare, n);
|
||||
len -= variantLen;
|
||||
if (variantLen > 0) {
|
||||
if (len > 0 && name[len-1] == '_') { /* delete trailing '_' */
|
||||
--len;
|
||||
}
|
||||
addKeyword = VARIANT_MAP[j].keyword;
|
||||
addValue = VARIANT_MAP[j].value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (len > 0 && len <= nameCapacity && name[len-1] == '_') { /* delete trailing '_' */
|
||||
--len;
|
||||
}
|
||||
}
|
||||
|
||||
/* Look up the ID in the canonicalization map */
|
||||
for (j=0; j<UPRV_LENGTHOF(CANONICALIZE_MAP); j++) {
|
||||
const char* id = CANONICALIZE_MAP[j].id;
|
||||
|
|
@ -1894,10 +1738,6 @@ _canonicalize(const char* localeID,
|
|||
break; /* Don't remap "" if keywords present */
|
||||
}
|
||||
len = _copyCount(name, nameCapacity, CANONICALIZE_MAP[j].canonicalID);
|
||||
if (CANONICALIZE_MAP[j].keyword) {
|
||||
addKeyword = CANONICALIZE_MAP[j].keyword;
|
||||
addValue = CANONICALIZE_MAP[j].value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1912,14 +1752,7 @@ _canonicalize(const char* localeID,
|
|||
++len;
|
||||
++fieldCount;
|
||||
len += _getKeywords(tmpLocaleID+1, '@', (len<nameCapacity ? name+len : NULL), nameCapacity-len,
|
||||
NULL, 0, NULL, TRUE, addKeyword, addValue, err);
|
||||
} else if (addKeyword != NULL) {
|
||||
U_ASSERT(addValue != NULL && len < nameCapacity);
|
||||
/* inelegant but works -- later make _getKeywords do this? */
|
||||
len += _copyCount(name+len, nameCapacity-len, "@");
|
||||
len += _copyCount(name+len, nameCapacity-len, addKeyword);
|
||||
len += _copyCount(name+len, nameCapacity-len, "=");
|
||||
len += _copyCount(name+len, nameCapacity-len, addValue);
|
||||
NULL, 0, NULL, TRUE, err);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1954,9 +1787,16 @@ uloc_getParent(const char* localeID,
|
|||
i=0;
|
||||
}
|
||||
|
||||
if(i>0 && parent != localeID) {
|
||||
uprv_memcpy(parent, localeID, uprv_min(i, parentCapacity));
|
||||
if (i > 0) {
|
||||
if (uprv_strnicmp(localeID, "und_", 4) == 0) {
|
||||
localeID += 3;
|
||||
i -= 3;
|
||||
uprv_memmove(parent, localeID, uprv_min(i, parentCapacity));
|
||||
} else if (parent != localeID) {
|
||||
uprv_memcpy(parent, localeID, uprv_min(i, parentCapacity));
|
||||
}
|
||||
}
|
||||
|
||||
return u_terminateChars(parent, parentCapacity, i, err);
|
||||
}
|
||||
|
||||
|
|
@ -2172,23 +2012,14 @@ uloc_getLCID(const char* localeID)
|
|||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
char langID[ULOC_FULLNAME_CAPACITY];
|
||||
uint32_t lcid = 0;
|
||||
|
||||
/* Check for incomplete id. */
|
||||
if (!localeID || uprv_strlen(localeID) < 2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Attempt platform lookup if available
|
||||
lcid = uprv_convertToLCIDPlatform(localeID);
|
||||
if (lcid > 0)
|
||||
{
|
||||
// Windows found an LCID, return that
|
||||
return lcid;
|
||||
}
|
||||
|
||||
uloc_getLanguage(localeID, langID, sizeof(langID), &status);
|
||||
if (U_FAILURE(status)) {
|
||||
if (U_FAILURE(status) || status == U_STRING_NOT_TERMINATED_WARNING) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@
|
|||
|
||||
#include "unicode/utypes.h"
|
||||
|
||||
#if U_PLATFORM_USES_ONLY_WIN32_API
|
||||
// This file contains only desktop Windows behavior
|
||||
// Windows UWP calls Windows::Globalization directly, so this isn't needed there.
|
||||
#if U_PLATFORM_USES_ONLY_WIN32_API && (U_PLATFORM_HAS_WINUWP_API == 0)
|
||||
|
||||
#include "wintz.h"
|
||||
#include "cmemory.h"
|
||||
|
|
@ -21,7 +23,6 @@
|
|||
|
||||
#include "unicode/ures.h"
|
||||
#include "unicode/ustring.h"
|
||||
#include "uresimp.h"
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
|
|
@ -33,94 +34,362 @@
|
|||
# define NOMCX
|
||||
#include <windows.h>
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
#define MAX_LENGTH_ID 40
|
||||
|
||||
// The value of MAX_TIMEZONE_ID_LENGTH is 128, which is defined in DYNAMIC_TIME_ZONE_INFORMATION
|
||||
#define MAX_TIMEZONE_ID_LENGTH 128
|
||||
/* The layout of the Tzi value in the registry */
|
||||
typedef struct
|
||||
{
|
||||
int32_t bias;
|
||||
int32_t standardBias;
|
||||
int32_t daylightBias;
|
||||
SYSTEMTIME standardDate;
|
||||
SYSTEMTIME daylightDate;
|
||||
} TZI;
|
||||
|
||||
/**
|
||||
* Main Windows time zone detection function.
|
||||
* Returns the Windows time zone converted to an ICU time zone as a heap-allocated buffer, or nullptr upon failure.
|
||||
* Note: We use the Win32 API GetDynamicTimeZoneInformation to get the current time zone info.
|
||||
* This API returns a non-localized time zone name, which we can then map to an ICU time zone name.
|
||||
* Various registry keys and key fragments.
|
||||
*/
|
||||
static const wchar_t CURRENT_ZONE_REGKEY[] = L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation\\";
|
||||
static const char TZI_REGKEY[] = "TZI";
|
||||
static const char STD_REGKEY[] = "Std";
|
||||
|
||||
/**
|
||||
* The time zone root keys (under HKLM) for Win7+
|
||||
*/
|
||||
static const char TZ_REGKEY[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones\\";
|
||||
|
||||
static LONG openTZRegKey(HKEY *hkey, const char *winid)
|
||||
{
|
||||
char subKeyName[110]; /* TODO: why 110?? */
|
||||
char *name;
|
||||
LONG result;
|
||||
|
||||
uprv_strcpy(subKeyName, TZ_REGKEY);
|
||||
name = &subKeyName[strlen(subKeyName)];
|
||||
uprv_strcat(subKeyName, winid);
|
||||
|
||||
result = RegOpenKeyExA(HKEY_LOCAL_MACHINE,
|
||||
subKeyName,
|
||||
0,
|
||||
KEY_QUERY_VALUE,
|
||||
hkey);
|
||||
return result;
|
||||
}
|
||||
|
||||
static LONG getTZI(const char *winid, TZI *tzi)
|
||||
{
|
||||
DWORD cbData = sizeof(TZI);
|
||||
LONG result;
|
||||
HKEY hkey;
|
||||
|
||||
result = openTZRegKey(&hkey, winid);
|
||||
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
result = RegQueryValueExA(hkey,
|
||||
TZI_REGKEY,
|
||||
NULL,
|
||||
NULL,
|
||||
(LPBYTE)tzi,
|
||||
&cbData);
|
||||
RegCloseKey(hkey);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static LONG getSTDName(const char *winid, char *regStdName, int32_t length)
|
||||
{
|
||||
DWORD cbData = length;
|
||||
LONG result;
|
||||
HKEY hkey;
|
||||
|
||||
result = openTZRegKey(&hkey, winid);
|
||||
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
result = RegQueryValueExA(hkey,
|
||||
STD_REGKEY,
|
||||
NULL,
|
||||
NULL,
|
||||
(LPBYTE)regStdName,
|
||||
&cbData);
|
||||
RegCloseKey(hkey);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static LONG getTZKeyName(char* tzKeyName, int32_t tzKeyNamelength)
|
||||
{
|
||||
HKEY hkey;
|
||||
LONG result = FALSE;
|
||||
WCHAR timeZoneKeyNameData[128];
|
||||
DWORD timeZoneKeyNameLength = static_cast<DWORD>(sizeof(timeZoneKeyNameData));
|
||||
|
||||
if(ERROR_SUCCESS == RegOpenKeyExW(
|
||||
HKEY_LOCAL_MACHINE,
|
||||
CURRENT_ZONE_REGKEY,
|
||||
0,
|
||||
KEY_QUERY_VALUE,
|
||||
&hkey))
|
||||
{
|
||||
if (ERROR_SUCCESS == RegQueryValueExW(
|
||||
hkey,
|
||||
L"TimeZoneKeyName",
|
||||
NULL,
|
||||
NULL,
|
||||
(LPBYTE)timeZoneKeyNameData,
|
||||
&timeZoneKeyNameLength))
|
||||
{
|
||||
// Ensure null termination.
|
||||
timeZoneKeyNameData[UPRV_LENGTHOF(timeZoneKeyNameData) - 1] = L'\0';
|
||||
|
||||
// Convert the UTF-16 string to UTF-8.
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
u_strToUTF8(tzKeyName, tzKeyNamelength, NULL, reinterpret_cast<const UChar *>(timeZoneKeyNameData), -1, &status);
|
||||
if (U_ZERO_ERROR == status)
|
||||
{
|
||||
result = ERROR_SUCCESS;
|
||||
}
|
||||
}
|
||||
RegCloseKey(hkey);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
This code attempts to detect the Windows time zone directly,
|
||||
as set in the Windows Date and Time control panel. It attempts
|
||||
to work on versions greater than Windows Vista and on localized
|
||||
installs. It works by directly interrogating the registry and
|
||||
comparing the data there with the data returned by the
|
||||
GetTimeZoneInformation API, along with some other strategies. The
|
||||
registry contains time zone data under this key:
|
||||
|
||||
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\
|
||||
|
||||
Under this key are several subkeys, one for each time zone. For
|
||||
example these subkeys are named "Pacific Standard Time" on Vista+.
|
||||
There are some other wrinkles; see the code for
|
||||
details. The subkey name is NOT LOCALIZED, allowing us to support
|
||||
localized installs.
|
||||
|
||||
Under the subkey are data values. We care about:
|
||||
|
||||
Std Standard time display name, localized
|
||||
TZI Binary block of data
|
||||
|
||||
The TZI data is of particular interest. It contains the offset, two
|
||||
more offsets for standard and daylight time, and the start and end
|
||||
rules. This is the same data returned by the GetTimeZoneInformation
|
||||
API. The API may modify the data on the way out, so we have to be
|
||||
careful, but essentially we do a binary comparison against the TZI
|
||||
blocks of various registry keys. When we find a match, we know what
|
||||
time zone Windows is set to. Since the registry key is not
|
||||
localized, we can then translate the key through a simple table
|
||||
lookup into the corresponding ICU time zone.
|
||||
|
||||
This strategy doesn't always work because there are zones which
|
||||
share an offset and rules, so more than one TZI block will match.
|
||||
For example, both Tokyo and Seoul are at GMT+9 with no DST rules;
|
||||
their TZI blocks are identical. For these cases, we fall back to a
|
||||
name lookup. We attempt to match the display name as stored in the
|
||||
registry for the current zone to the display name stored in the
|
||||
registry for various Windows zones. By comparing the registry data
|
||||
directly we avoid conversion complications.
|
||||
|
||||
Author: Alan Liu
|
||||
Since: ICU 2.6
|
||||
Based on original code by Carl Brown <cbrown@xnetinc.com>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Main Windows time zone detection function. Returns the Windows
|
||||
* time zone, translated to an ICU time zone, or NULL upon failure.
|
||||
*/
|
||||
U_CFUNC const char* U_EXPORT2
|
||||
uprv_detectWindowsTimeZone()
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
char* icuid = nullptr;
|
||||
char dynamicTZKeyName[MAX_TIMEZONE_ID_LENGTH];
|
||||
char tmpid[MAX_TIMEZONE_ID_LENGTH];
|
||||
UResourceBundle* bundle = NULL;
|
||||
char* icuid = NULL;
|
||||
char apiStdName[MAX_LENGTH_ID];
|
||||
char regStdName[MAX_LENGTH_ID];
|
||||
char tmpid[MAX_LENGTH_ID];
|
||||
int32_t len;
|
||||
int id = GEOID_NOT_AVAILABLE;
|
||||
int id;
|
||||
int errorCode;
|
||||
wchar_t ISOcodeW[3] = {}; /* 2 letter ISO code in UTF-16 */
|
||||
char ISOcode[3] = {}; /* 2 letter ISO code in UTF-8 */
|
||||
wchar_t ISOcodeW[3]; /* 2 letter iso code in UTF-16*/
|
||||
char ISOcodeA[3]; /* 2 letter iso code in ansi */
|
||||
|
||||
DYNAMIC_TIME_ZONE_INFORMATION dynamicTZI;
|
||||
uprv_memset(&dynamicTZI, 0, sizeof(dynamicTZI));
|
||||
uprv_memset(dynamicTZKeyName, 0, sizeof(dynamicTZKeyName));
|
||||
uprv_memset(tmpid, 0, sizeof(tmpid));
|
||||
LONG result;
|
||||
TZI tziKey;
|
||||
TZI tziReg;
|
||||
TIME_ZONE_INFORMATION apiTZI;
|
||||
|
||||
/* Obtain TIME_ZONE_INFORMATION from the API and get the non-localized time zone name. */
|
||||
if (TIME_ZONE_ID_INVALID == GetDynamicTimeZoneInformation(&dynamicTZI)) {
|
||||
return nullptr;
|
||||
}
|
||||
BOOL tryPreVistaFallback;
|
||||
OSVERSIONINFO osVerInfo;
|
||||
|
||||
/* Obtain TIME_ZONE_INFORMATION from the API, and then convert it
|
||||
to TZI. We could also interrogate the registry directly; we do
|
||||
this below if needed. */
|
||||
uprv_memset(&apiTZI, 0, sizeof(apiTZI));
|
||||
uprv_memset(&tziKey, 0, sizeof(tziKey));
|
||||
uprv_memset(&tziReg, 0, sizeof(tziReg));
|
||||
GetTimeZoneInformation(&apiTZI);
|
||||
tziKey.bias = apiTZI.Bias;
|
||||
uprv_memcpy((char *)&tziKey.standardDate, (char*)&apiTZI.StandardDate,
|
||||
sizeof(apiTZI.StandardDate));
|
||||
uprv_memcpy((char *)&tziKey.daylightDate, (char*)&apiTZI.DaylightDate,
|
||||
sizeof(apiTZI.DaylightDate));
|
||||
|
||||
/* Convert the wchar_t* standard name to char* */
|
||||
uprv_memset(apiStdName, 0, sizeof(apiStdName));
|
||||
wcstombs(apiStdName, apiTZI.StandardName, MAX_LENGTH_ID);
|
||||
|
||||
tmpid[0] = 0;
|
||||
|
||||
id = GetUserGeoID(GEOCLASS_NATION);
|
||||
errorCode = GetGeoInfoW(id, GEO_ISO2, ISOcodeW, 3, 0);
|
||||
u_strToUTF8(ISOcodeA, 3, NULL, (const UChar *)ISOcodeW, 3, &status);
|
||||
|
||||
// convert from wchar_t* (UTF-16 on Windows) to char* (UTF-8).
|
||||
u_strToUTF8(ISOcode, UPRV_LENGTHOF(ISOcode), nullptr,
|
||||
reinterpret_cast<const UChar*>(ISOcodeW), UPRV_LENGTHOF(ISOcodeW), &status);
|
||||
bundle = ures_openDirect(NULL, "windowsZones", &status);
|
||||
ures_getByKey(bundle, "mapTimezones", bundle, &status);
|
||||
|
||||
LocalUResourceBundlePointer bundle(ures_openDirect(nullptr, "windowsZones", &status));
|
||||
ures_getByKey(bundle.getAlias(), "mapTimezones", bundle.getAlias(), &status);
|
||||
|
||||
// convert from wchar_t* (UTF-16 on Windows) to char* (UTF-8).
|
||||
u_strToUTF8(dynamicTZKeyName, UPRV_LENGTHOF(dynamicTZKeyName), nullptr,
|
||||
reinterpret_cast<const UChar*>(dynamicTZI.TimeZoneKeyName), UPRV_LENGTHOF(dynamicTZI.TimeZoneKeyName), &status);
|
||||
|
||||
if (U_FAILURE(status)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (dynamicTZI.TimeZoneKeyName[0] != 0) {
|
||||
UResourceBundle winTZ;
|
||||
ures_initStackObject(&winTZ);
|
||||
ures_getByKey(bundle.getAlias(), dynamicTZKeyName, &winTZ, &status);
|
||||
|
||||
if (U_SUCCESS(status)) {
|
||||
const UChar* icuTZ = nullptr;
|
||||
if (errorCode != 0) {
|
||||
icuTZ = ures_getStringByKey(&winTZ, ISOcode, &len, &status);
|
||||
/*
|
||||
Windows Vista+ provides us with a "TimeZoneKeyName" that is not localized
|
||||
and can be used to directly map a name in our bundle. Try to use that first
|
||||
if we're on Vista or higher
|
||||
*/
|
||||
uprv_memset(&osVerInfo, 0, sizeof(osVerInfo));
|
||||
osVerInfo.dwOSVersionInfoSize = sizeof(osVerInfo);
|
||||
tryPreVistaFallback = TRUE;
|
||||
result = getTZKeyName(regStdName, sizeof(regStdName));
|
||||
if(ERROR_SUCCESS == result)
|
||||
{
|
||||
UResourceBundle* winTZ = ures_getByKey(bundle, regStdName, NULL, &status);
|
||||
if(U_SUCCESS(status))
|
||||
{
|
||||
const UChar* icuTZ = NULL;
|
||||
if (errorCode != 0)
|
||||
{
|
||||
icuTZ = ures_getStringByKey(winTZ, ISOcodeA, &len, &status);
|
||||
}
|
||||
if (errorCode == 0 || icuTZ == nullptr) {
|
||||
if (errorCode==0 || icuTZ==NULL)
|
||||
{
|
||||
/* fallback to default "001" and reset status */
|
||||
status = U_ZERO_ERROR;
|
||||
icuTZ = ures_getStringByKey(&winTZ, "001", &len, &status);
|
||||
icuTZ = ures_getStringByKey(winTZ, "001", &len, &status);
|
||||
}
|
||||
|
||||
if (U_SUCCESS(status)) {
|
||||
int index = 0;
|
||||
|
||||
while (!(*icuTZ == '\0' || *icuTZ == ' ')) {
|
||||
// time zone IDs only contain ASCII invariant characters.
|
||||
tmpid[index++] = (char)(*icuTZ++);
|
||||
if(U_SUCCESS(status))
|
||||
{
|
||||
int index=0;
|
||||
while (! (*icuTZ == '\0' || *icuTZ ==' '))
|
||||
{
|
||||
tmpid[index++]=(char)(*icuTZ++); /* safe to assume 'char' is ASCII compatible on windows */
|
||||
}
|
||||
tmpid[index] = '\0';
|
||||
tmpid[index]='\0';
|
||||
tryPreVistaFallback = FALSE;
|
||||
}
|
||||
}
|
||||
ures_close(&winTZ);
|
||||
ures_close(winTZ);
|
||||
}
|
||||
|
||||
// Copy the timezone ID to icuid to be returned.
|
||||
if (tmpid[0] != 0) {
|
||||
icuid = uprv_strdup(tmpid);
|
||||
if(tryPreVistaFallback)
|
||||
{
|
||||
/* Note: We get the winid not from static tables but from resource bundle. */
|
||||
while (U_SUCCESS(status) && ures_hasNext(bundle))
|
||||
{
|
||||
UBool idFound = FALSE;
|
||||
const char* winid;
|
||||
UResourceBundle* winTZ = ures_getNextResource(bundle, NULL, &status);
|
||||
if (U_FAILURE(status))
|
||||
{
|
||||
break;
|
||||
}
|
||||
winid = ures_getKey(winTZ);
|
||||
result = getTZI(winid, &tziReg);
|
||||
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
/* Windows alters the DaylightBias in some situations.
|
||||
Using the bias and the rules suffices, so overwrite
|
||||
these unreliable fields. */
|
||||
tziKey.standardBias = tziReg.standardBias;
|
||||
tziKey.daylightBias = tziReg.daylightBias;
|
||||
|
||||
if (uprv_memcmp((char *)&tziKey, (char*)&tziReg, sizeof(tziKey)) == 0)
|
||||
{
|
||||
const UChar* icuTZ = NULL;
|
||||
if (errorCode != 0)
|
||||
{
|
||||
icuTZ = ures_getStringByKey(winTZ, ISOcodeA, &len, &status);
|
||||
}
|
||||
if (errorCode==0 || icuTZ==NULL)
|
||||
{
|
||||
/* fallback to default "001" and reset status */
|
||||
status = U_ZERO_ERROR;
|
||||
icuTZ = ures_getStringByKey(winTZ, "001", &len, &status);
|
||||
}
|
||||
|
||||
if (U_SUCCESS(status))
|
||||
{
|
||||
/* Get the standard name from the registry key to compare with
|
||||
the one from Windows API call. */
|
||||
uprv_memset(regStdName, 0, sizeof(regStdName));
|
||||
result = getSTDName(winid, regStdName, sizeof(regStdName));
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
if (uprv_strcmp(apiStdName, regStdName) == 0)
|
||||
{
|
||||
idFound = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* tmpid buffer holds the ICU timezone ID corresponding to the timezone ID from Windows.
|
||||
* If none is found, tmpid buffer will contain a fallback ID (i.e. the time zone ID matching
|
||||
* the current time zone information)
|
||||
*/
|
||||
if (idFound || tmpid[0] == 0)
|
||||
{
|
||||
/* if icuTZ has more than one city, take only the first (i.e. terminate icuTZ at first space) */
|
||||
int index=0;
|
||||
while (! (*icuTZ == '\0' || *icuTZ ==' '))
|
||||
{
|
||||
tmpid[index++]=(char)(*icuTZ++); /* safe to assume 'char' is ASCII compatible on windows */
|
||||
}
|
||||
tmpid[index]='\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ures_close(winTZ);
|
||||
if (idFound)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy the timezone ID to icuid to be returned.
|
||||
*/
|
||||
if (tmpid[0] != 0)
|
||||
{
|
||||
len = uprv_strlen(tmpid);
|
||||
icuid = (char*)uprv_calloc(len + 1, sizeof(char));
|
||||
if (icuid != NULL)
|
||||
{
|
||||
uprv_strcpy(icuid, tmpid);
|
||||
}
|
||||
}
|
||||
|
||||
ures_close(bundle);
|
||||
|
||||
return icuid;
|
||||
}
|
||||
|
||||
U_NAMESPACE_END
|
||||
#endif /* U_PLATFORM_USES_ONLY_WIN32_API */
|
||||
#endif /* U_PLATFORM_USES_ONLY_WIN32_API && (U_PLATFORM_HAS_WINUWP_API == 0) */
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ U_CDECL_BEGIN
|
|||
typedef struct _TIME_ZONE_INFORMATION TIME_ZONE_INFORMATION;
|
||||
U_CDECL_END
|
||||
|
||||
U_CFUNC const char* U_EXPORT2
|
||||
U_INTERNAL const char* U_EXPORT2
|
||||
uprv_detectWindowsTimeZone();
|
||||
|
||||
#endif /* U_PLATFORM_USES_ONLY_WIN32_API */
|
||||
|
|
|
|||
|
|
@ -94,83 +94,12 @@ UnicodeString* Win32DateFormat::getTimeDateFormat(const Calendar *cal, const Loc
|
|||
return result;
|
||||
}
|
||||
|
||||
// TODO: This is copied in both winnmfmt.cpp and windtfmt.cpp, but really should
|
||||
// be factored out into a common helper for both.
|
||||
static UErrorCode GetEquivalentWindowsLocaleName(const Locale& locale, UnicodeString** buffer)
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
char asciiBCP47Tag[LOCALE_NAME_MAX_LENGTH] = {};
|
||||
|
||||
// Convert from names like "en_CA" and "de_DE@collation=phonebook" to "en-CA" and "de-DE-u-co-phonebk".
|
||||
(void)uloc_toLanguageTag(locale.getName(), asciiBCP47Tag, UPRV_LENGTHOF(asciiBCP47Tag), FALSE, &status);
|
||||
|
||||
if (U_SUCCESS(status))
|
||||
{
|
||||
// Need it to be UTF-16, not 8-bit
|
||||
// TODO: This seems like a good thing for a helper
|
||||
wchar_t bcp47Tag[LOCALE_NAME_MAX_LENGTH] = {};
|
||||
int32_t i;
|
||||
for (i = 0; i < UPRV_LENGTHOF(bcp47Tag); i++)
|
||||
{
|
||||
if (asciiBCP47Tag[i] == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// normally just copy the character
|
||||
bcp47Tag[i] = static_cast<wchar_t>(asciiBCP47Tag[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure it's null terminated
|
||||
if (i < (UPRV_LENGTHOF(bcp47Tag) - 1))
|
||||
{
|
||||
bcp47Tag[i] = L'\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ran out of room.
|
||||
bcp47Tag[UPRV_LENGTHOF(bcp47Tag) - 1] = L'\0';
|
||||
}
|
||||
|
||||
|
||||
wchar_t windowsLocaleName[LOCALE_NAME_MAX_LENGTH] = {};
|
||||
|
||||
// Note: On Windows versions below 10, there is no support for locale name aliases.
|
||||
// This means that it will fail for locales where ICU has a completely different
|
||||
// name (like ku vs ckb), and it will also not work for alternate sort locale
|
||||
// names like "de-DE-u-co-phonebk".
|
||||
|
||||
// TODO: We could add some sort of exception table for cases like ku vs ckb.
|
||||
|
||||
int length = ResolveLocaleName(bcp47Tag, windowsLocaleName, UPRV_LENGTHOF(windowsLocaleName));
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
*buffer = new UnicodeString(windowsLocaleName);
|
||||
}
|
||||
else
|
||||
{
|
||||
status = U_UNSUPPORTED_ERROR;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// TODO: Range-check timeStyle, dateStyle
|
||||
Win32DateFormat::Win32DateFormat(DateFormat::EStyle timeStyle, DateFormat::EStyle dateStyle, const Locale &locale, UErrorCode &status)
|
||||
: DateFormat(), fDateTimeMsg(NULL), fTimeStyle(timeStyle), fDateStyle(dateStyle), fLocale(locale), fZoneID(), fWindowsLocaleName(nullptr)
|
||||
: DateFormat(), fDateTimeMsg(NULL), fTimeStyle(timeStyle), fDateStyle(dateStyle), fLocale(locale), fZoneID()
|
||||
{
|
||||
if (U_SUCCESS(status)) {
|
||||
GetEquivalentWindowsLocaleName(locale, &fWindowsLocaleName);
|
||||
// Note: In the previous code, it would look up the LCID for the locale, and if
|
||||
// the locale was not recognized then it would get an LCID of 0, which is a
|
||||
// synonym for LOCALE_USER_DEFAULT on Windows.
|
||||
// If the above method fails, then fWindowsLocaleName will remain as nullptr, and
|
||||
// then we will pass nullptr to API GetLocaleInfoEx, which is the same as passing
|
||||
// LOCALE_USER_DEFAULT.
|
||||
|
||||
fLCID = locale.getLCID();
|
||||
fTZI = NEW_ARRAY(TIME_ZONE_INFORMATION, 1);
|
||||
uprv_memset(fTZI, 0, sizeof(TIME_ZONE_INFORMATION));
|
||||
adoptCalendar(Calendar::createInstance(locale, status));
|
||||
|
|
@ -188,7 +117,6 @@ Win32DateFormat::~Win32DateFormat()
|
|||
// delete fCalendar;
|
||||
uprv_free(fTZI);
|
||||
delete fDateTimeMsg;
|
||||
delete fWindowsLocaleName;
|
||||
}
|
||||
|
||||
Win32DateFormat &Win32DateFormat::operator=(const Win32DateFormat &other)
|
||||
|
|
@ -208,8 +136,6 @@ Win32DateFormat &Win32DateFormat::operator=(const Win32DateFormat &other)
|
|||
this->fTZI = NEW_ARRAY(TIME_ZONE_INFORMATION, 1);
|
||||
*this->fTZI = *other.fTZI;
|
||||
|
||||
this->fWindowsLocaleName = other.fWindowsLocaleName == NULL ? NULL : new UnicodeString(*other.fWindowsLocaleName);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
@ -309,22 +235,16 @@ void Win32DateFormat::formatDate(const SYSTEMTIME *st, UnicodeString &appendTo)
|
|||
int result=0;
|
||||
wchar_t stackBuffer[STACK_BUFFER_SIZE];
|
||||
wchar_t *buffer = stackBuffer;
|
||||
const wchar_t *localeName = nullptr;
|
||||
|
||||
if (fWindowsLocaleName != nullptr)
|
||||
{
|
||||
localeName = reinterpret_cast<const wchar_t*>(toOldUCharPtr(fWindowsLocaleName->getTerminatedBuffer()));
|
||||
}
|
||||
|
||||
result = GetDateFormatEx(localeName, dfFlags[fDateStyle - kDateOffset], st, NULL, buffer, STACK_BUFFER_SIZE, NULL);
|
||||
result = GetDateFormatW(fLCID, dfFlags[fDateStyle - kDateOffset], st, NULL, buffer, STACK_BUFFER_SIZE);
|
||||
|
||||
if (result == 0) {
|
||||
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
int newLength = GetDateFormatEx(localeName, dfFlags[fDateStyle - kDateOffset], st, NULL, NULL, 0, NULL);
|
||||
int newLength = GetDateFormatW(fLCID, dfFlags[fDateStyle - kDateOffset], st, NULL, NULL, 0);
|
||||
|
||||
buffer = NEW_ARRAY(wchar_t, newLength);
|
||||
|
||||
GetDateFormatEx(localeName, dfFlags[fDateStyle - kDateOffset], st, NULL, buffer, newLength, NULL);
|
||||
GetDateFormatW(fLCID, dfFlags[fDateStyle - kDateOffset], st, NULL, buffer, newLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -342,22 +262,16 @@ void Win32DateFormat::formatTime(const SYSTEMTIME *st, UnicodeString &appendTo)
|
|||
int result;
|
||||
wchar_t stackBuffer[STACK_BUFFER_SIZE];
|
||||
wchar_t *buffer = stackBuffer;
|
||||
const wchar_t *localeName = nullptr;
|
||||
|
||||
if (fWindowsLocaleName != nullptr)
|
||||
{
|
||||
localeName = reinterpret_cast<const wchar_t*>(toOldUCharPtr(fWindowsLocaleName->getTerminatedBuffer()));
|
||||
}
|
||||
|
||||
result = GetTimeFormatEx(localeName, tfFlags[fTimeStyle], st, NULL, buffer, STACK_BUFFER_SIZE);
|
||||
result = GetTimeFormatW(fLCID, tfFlags[fTimeStyle], st, NULL, buffer, STACK_BUFFER_SIZE);
|
||||
|
||||
if (result == 0) {
|
||||
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
int newLength = GetTimeFormatEx(localeName, tfFlags[fTimeStyle], st, NULL, NULL, 0);
|
||||
int newLength = GetTimeFormatW(fLCID, tfFlags[fTimeStyle], st, NULL, NULL, 0);
|
||||
|
||||
buffer = NEW_ARRAY(wchar_t, newLength);
|
||||
|
||||
GetTimeFormatEx(localeName, tfFlags[fTimeStyle], st, NULL, buffer, newLength);
|
||||
GetDateFormatW(fLCID, tfFlags[fTimeStyle], st, NULL, buffer, newLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -124,10 +124,9 @@ private:
|
|||
DateFormat::EStyle fTimeStyle;
|
||||
DateFormat::EStyle fDateStyle;
|
||||
Locale fLocale;
|
||||
int32_t fLCID;
|
||||
UnicodeString fZoneID;
|
||||
TIME_ZONE_INFORMATION *fTZI;
|
||||
|
||||
UnicodeString* fWindowsLocaleName; // Stores the equivalent Windows locale name.
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
|
|
|||
|
|
@ -60,43 +60,43 @@ UOBJECT_DEFINE_RTTI_IMPLEMENTATION(Win32NumberFormat)
|
|||
* end in ";0" then the return value should be multiplied by 10.
|
||||
* (e.g. "3" => 30, "3;2" => 320)
|
||||
*/
|
||||
static UINT getGrouping(const wchar_t *grouping)
|
||||
static UINT getGrouping(const char *grouping)
|
||||
{
|
||||
UINT g = 0;
|
||||
const wchar_t *s;
|
||||
const char *s;
|
||||
|
||||
for (s = grouping; *s != L'\0'; s += 1) {
|
||||
if (*s > L'0' && *s < L'9') {
|
||||
g = g * 10 + (*s - L'0');
|
||||
} else if (*s != L';') {
|
||||
for (s = grouping; *s != '\0'; s += 1) {
|
||||
if (*s > '0' && *s < '9') {
|
||||
g = g * 10 + (*s - '0');
|
||||
} else if (*s != ';') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*s != L'0') {
|
||||
if (*s != '0') {
|
||||
g *= 10;
|
||||
}
|
||||
|
||||
return g;
|
||||
}
|
||||
|
||||
static void getNumberFormat(NUMBERFMTW *fmt, const wchar_t *windowsLocaleName)
|
||||
static void getNumberFormat(NUMBERFMTW *fmt, int32_t lcid)
|
||||
{
|
||||
wchar_t buf[10];
|
||||
char buf[10];
|
||||
|
||||
GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_IDIGITS, (LPWSTR) &fmt->NumDigits, sizeof(UINT));
|
||||
GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR) &fmt->LeadingZero, sizeof(UINT));
|
||||
GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_IDIGITS, (LPWSTR) &fmt->NumDigits, sizeof(UINT));
|
||||
GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR) &fmt->LeadingZero, sizeof(UINT));
|
||||
|
||||
GetLocaleInfoEx(windowsLocaleName, LOCALE_SGROUPING, (LPWSTR)buf, 10);
|
||||
GetLocaleInfoA(lcid, LOCALE_SGROUPING, buf, 10);
|
||||
fmt->Grouping = getGrouping(buf);
|
||||
|
||||
fmt->lpDecimalSep = NEW_ARRAY(wchar_t, 6);
|
||||
GetLocaleInfoEx(windowsLocaleName, LOCALE_SDECIMAL, fmt->lpDecimalSep, 6);
|
||||
GetLocaleInfoW(lcid, LOCALE_SDECIMAL, fmt->lpDecimalSep, 6);
|
||||
|
||||
fmt->lpThousandSep = NEW_ARRAY(wchar_t, 6);
|
||||
GetLocaleInfoEx(windowsLocaleName, LOCALE_STHOUSAND, fmt->lpThousandSep, 6);
|
||||
GetLocaleInfoW(lcid, LOCALE_STHOUSAND, fmt->lpThousandSep, 6);
|
||||
|
||||
GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_INEGNUMBER, (LPWSTR) &fmt->NegativeOrder, sizeof(UINT));
|
||||
GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_INEGNUMBER, (LPWSTR) &fmt->NegativeOrder, sizeof(UINT));
|
||||
}
|
||||
|
||||
static void freeNumberFormat(NUMBERFMTW *fmt)
|
||||
|
|
@ -107,27 +107,27 @@ static void freeNumberFormat(NUMBERFMTW *fmt)
|
|||
}
|
||||
}
|
||||
|
||||
static void getCurrencyFormat(CURRENCYFMTW *fmt, const wchar_t *windowsLocaleName)
|
||||
static void getCurrencyFormat(CURRENCYFMTW *fmt, int32_t lcid)
|
||||
{
|
||||
wchar_t buf[10];
|
||||
char buf[10];
|
||||
|
||||
GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_ICURRDIGITS, (LPWSTR) &fmt->NumDigits, sizeof(UINT));
|
||||
GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR) &fmt->LeadingZero, sizeof(UINT));
|
||||
GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ICURRDIGITS, (LPWSTR) &fmt->NumDigits, sizeof(UINT));
|
||||
GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ILZERO, (LPWSTR) &fmt->LeadingZero, sizeof(UINT));
|
||||
|
||||
GetLocaleInfoEx(windowsLocaleName, LOCALE_SMONGROUPING, (LPWSTR)buf, sizeof(buf));
|
||||
GetLocaleInfoA(lcid, LOCALE_SMONGROUPING, buf, sizeof(buf));
|
||||
fmt->Grouping = getGrouping(buf);
|
||||
|
||||
fmt->lpDecimalSep = NEW_ARRAY(wchar_t, 6);
|
||||
GetLocaleInfoEx(windowsLocaleName, LOCALE_SMONDECIMALSEP, fmt->lpDecimalSep, 6);
|
||||
GetLocaleInfoW(lcid, LOCALE_SMONDECIMALSEP, fmt->lpDecimalSep, 6);
|
||||
|
||||
fmt->lpThousandSep = NEW_ARRAY(wchar_t, 6);
|
||||
GetLocaleInfoEx(windowsLocaleName, LOCALE_SMONTHOUSANDSEP, fmt->lpThousandSep, 6);
|
||||
GetLocaleInfoW(lcid, LOCALE_SMONTHOUSANDSEP, fmt->lpThousandSep, 6);
|
||||
|
||||
GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_INEGCURR, (LPWSTR) &fmt->NegativeOrder, sizeof(UINT));
|
||||
GetLocaleInfoEx(windowsLocaleName, LOCALE_RETURN_NUMBER|LOCALE_ICURRENCY, (LPWSTR) &fmt->PositiveOrder, sizeof(UINT));
|
||||
GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_INEGCURR, (LPWSTR) &fmt->NegativeOrder, sizeof(UINT));
|
||||
GetLocaleInfoW(lcid, LOCALE_RETURN_NUMBER|LOCALE_ICURRENCY, (LPWSTR) &fmt->PositiveOrder, sizeof(UINT));
|
||||
|
||||
fmt->lpCurrencySymbol = NEW_ARRAY(wchar_t, 8);
|
||||
GetLocaleInfoEx(windowsLocaleName, LOCALE_SCURRENCY, (LPWSTR) fmt->lpCurrencySymbol, 8);
|
||||
GetLocaleInfoW(lcid, LOCALE_SCURRENCY, (LPWSTR) fmt->lpCurrencySymbol, 8);
|
||||
}
|
||||
|
||||
static void freeCurrencyFormat(CURRENCYFMTW *fmt)
|
||||
|
|
@ -139,83 +139,12 @@ static void freeCurrencyFormat(CURRENCYFMTW *fmt)
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: This is copied in both winnmfmt.cpp and windtfmt.cpp, but really should
|
||||
// be factored out into a common helper for both.
|
||||
static UErrorCode GetEquivalentWindowsLocaleName(const Locale& locale, UnicodeString** buffer)
|
||||
{
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
char asciiBCP47Tag[LOCALE_NAME_MAX_LENGTH] = {};
|
||||
|
||||
// Convert from names like "en_CA" and "de_DE@collation=phonebook" to "en-CA" and "de-DE-u-co-phonebk".
|
||||
(void) uloc_toLanguageTag(locale.getName(), asciiBCP47Tag, UPRV_LENGTHOF(asciiBCP47Tag), FALSE, &status);
|
||||
|
||||
if (U_SUCCESS(status))
|
||||
{
|
||||
// Need it to be UTF-16, not 8-bit
|
||||
// TODO: This seems like a good thing for a helper
|
||||
wchar_t bcp47Tag[LOCALE_NAME_MAX_LENGTH] = {};
|
||||
int32_t i;
|
||||
for (i = 0; i < UPRV_LENGTHOF(bcp47Tag); i++)
|
||||
{
|
||||
if (asciiBCP47Tag[i] == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// normally just copy the character
|
||||
bcp47Tag[i] = static_cast<wchar_t>(asciiBCP47Tag[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure it's null terminated
|
||||
if (i < (UPRV_LENGTHOF(bcp47Tag) - 1))
|
||||
{
|
||||
bcp47Tag[i] = L'\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ran out of room.
|
||||
bcp47Tag[UPRV_LENGTHOF(bcp47Tag) - 1] = L'\0';
|
||||
}
|
||||
|
||||
|
||||
wchar_t windowsLocaleName[LOCALE_NAME_MAX_LENGTH] = {};
|
||||
|
||||
// Note: On Windows versions below 10, there is no support for locale name aliases.
|
||||
// This means that it will fail for locales where ICU has a completely different
|
||||
// name (like ku vs ckb), and it will also not work for alternate sort locale
|
||||
// names like "de-DE-u-co-phonebk".
|
||||
|
||||
// TODO: We could add some sort of exception table for cases like ku vs ckb.
|
||||
|
||||
int length = ResolveLocaleName(bcp47Tag, windowsLocaleName, UPRV_LENGTHOF(windowsLocaleName));
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
*buffer = new UnicodeString(windowsLocaleName);
|
||||
}
|
||||
else
|
||||
{
|
||||
status = U_UNSUPPORTED_ERROR;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Win32NumberFormat::Win32NumberFormat(const Locale &locale, UBool currency, UErrorCode &status)
|
||||
: NumberFormat(), fCurrency(currency), fFormatInfo(NULL), fFractionDigitsSet(FALSE), fWindowsLocaleName(nullptr)
|
||||
: NumberFormat(), fCurrency(currency), fFormatInfo(NULL), fFractionDigitsSet(FALSE)
|
||||
{
|
||||
if (!U_FAILURE(status)) {
|
||||
fLCID = locale.getLCID();
|
||||
|
||||
GetEquivalentWindowsLocaleName(locale, &fWindowsLocaleName);
|
||||
// Note: In the previous code, it would look up the LCID for the locale, and if
|
||||
// the locale was not recognized then it would get an LCID of 0, which is a
|
||||
// synonym for LOCALE_USER_DEFAULT on Windows.
|
||||
// If the above method fails, then fWindowsLocaleName will remain as nullptr, and
|
||||
// then we will pass nullptr to API GetLocaleInfoEx, which is the same as passing
|
||||
// LOCALE_USER_DEFAULT.
|
||||
|
||||
// Resolve actual locale to be used later
|
||||
UErrorCode tmpsts = U_ZERO_ERROR;
|
||||
|
|
@ -226,19 +155,12 @@ Win32NumberFormat::Win32NumberFormat(const Locale &locale, UBool currency, UErro
|
|||
fLocale = Locale((const char*)tmpLocID);
|
||||
}
|
||||
|
||||
const wchar_t *localeName = nullptr;
|
||||
|
||||
if (fWindowsLocaleName != nullptr)
|
||||
{
|
||||
localeName = reinterpret_cast<const wchar_t*>(toOldUCharPtr(fWindowsLocaleName->getTerminatedBuffer()));
|
||||
}
|
||||
|
||||
fFormatInfo = (FormatInfo*)uprv_malloc(sizeof(FormatInfo));
|
||||
|
||||
if (fCurrency) {
|
||||
getCurrencyFormat(&fFormatInfo->currency, localeName);
|
||||
getCurrencyFormat(&fFormatInfo->currency, fLCID);
|
||||
} else {
|
||||
getNumberFormat(&fFormatInfo->number, localeName);
|
||||
getNumberFormat(&fFormatInfo->number, fLCID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -263,7 +185,6 @@ Win32NumberFormat::~Win32NumberFormat()
|
|||
|
||||
uprv_free(fFormatInfo);
|
||||
}
|
||||
delete fWindowsLocaleName;
|
||||
}
|
||||
|
||||
Win32NumberFormat &Win32NumberFormat::operator=(const Win32NumberFormat &other)
|
||||
|
|
@ -274,21 +195,13 @@ Win32NumberFormat &Win32NumberFormat::operator=(const Win32NumberFormat &other)
|
|||
this->fLocale = other.fLocale;
|
||||
this->fLCID = other.fLCID;
|
||||
this->fFractionDigitsSet = other.fFractionDigitsSet;
|
||||
this->fWindowsLocaleName = other.fWindowsLocaleName == NULL ? NULL : new UnicodeString(*other.fWindowsLocaleName);
|
||||
|
||||
const wchar_t *localeName = nullptr;
|
||||
|
||||
if (fWindowsLocaleName != nullptr)
|
||||
{
|
||||
localeName = reinterpret_cast<const wchar_t*>(toOldUCharPtr(fWindowsLocaleName->getTerminatedBuffer()));
|
||||
}
|
||||
|
||||
if (fCurrency) {
|
||||
freeCurrencyFormat(&fFormatInfo->currency);
|
||||
getCurrencyFormat(&fFormatInfo->currency, localeName);
|
||||
getCurrencyFormat(&fFormatInfo->currency, fLCID);
|
||||
} else {
|
||||
freeNumberFormat(&fFormatInfo->number);
|
||||
getNumberFormat(&fFormatInfo->number, localeName);
|
||||
getNumberFormat(&fFormatInfo->number, fLCID);
|
||||
}
|
||||
|
||||
return *this;
|
||||
|
|
@ -389,13 +302,6 @@ UnicodeString &Win32NumberFormat::format(int32_t numDigits, UnicodeString &appen
|
|||
formatInfo = *fFormatInfo;
|
||||
buffer[0] = 0x0000;
|
||||
|
||||
const wchar_t *localeName = nullptr;
|
||||
|
||||
if (fWindowsLocaleName != nullptr)
|
||||
{
|
||||
localeName = reinterpret_cast<const wchar_t*>(toOldUCharPtr(fWindowsLocaleName->getTerminatedBuffer()));
|
||||
}
|
||||
|
||||
if (fCurrency) {
|
||||
if (fFractionDigitsSet) {
|
||||
formatInfo.currency.NumDigits = (UINT) numDigits;
|
||||
|
|
@ -405,17 +311,17 @@ UnicodeString &Win32NumberFormat::format(int32_t numDigits, UnicodeString &appen
|
|||
formatInfo.currency.Grouping = 0;
|
||||
}
|
||||
|
||||
result = GetCurrencyFormatEx(localeName, 0, nBuffer, &formatInfo.currency, buffer, STACK_BUFFER_SIZE);
|
||||
result = GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, buffer, STACK_BUFFER_SIZE);
|
||||
|
||||
if (result == 0) {
|
||||
DWORD lastError = GetLastError();
|
||||
|
||||
if (lastError == ERROR_INSUFFICIENT_BUFFER) {
|
||||
int newLength = GetCurrencyFormatEx(localeName, 0, nBuffer, &formatInfo.currency, NULL, 0);
|
||||
int newLength = GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, NULL, 0);
|
||||
|
||||
buffer = NEW_ARRAY(wchar_t, newLength);
|
||||
buffer[0] = 0x0000;
|
||||
GetCurrencyFormatEx(localeName, 0, nBuffer, &formatInfo.currency, buffer, newLength);
|
||||
GetCurrencyFormatW(fLCID, 0, nBuffer, &formatInfo.currency, buffer, newLength);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -427,15 +333,15 @@ UnicodeString &Win32NumberFormat::format(int32_t numDigits, UnicodeString &appen
|
|||
formatInfo.number.Grouping = 0;
|
||||
}
|
||||
|
||||
result = GetNumberFormatEx(localeName, 0, nBuffer, &formatInfo.number, buffer, STACK_BUFFER_SIZE);
|
||||
result = GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, buffer, STACK_BUFFER_SIZE);
|
||||
|
||||
if (result == 0) {
|
||||
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
|
||||
int newLength = GetNumberFormatEx(localeName, 0, nBuffer, &formatInfo.number, NULL, 0);
|
||||
int newLength = GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, NULL, 0);
|
||||
|
||||
buffer = NEW_ARRAY(wchar_t, newLength);
|
||||
buffer[0] = 0x0000;
|
||||
GetNumberFormatEx(localeName, 0, nBuffer, &formatInfo.number, buffer, newLength);
|
||||
GetNumberFormatW(fLCID, 0, nBuffer, &formatInfo.number, buffer, newLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,8 +154,6 @@ private:
|
|||
int32_t fLCID;
|
||||
FormatInfo *fFormatInfo;
|
||||
UBool fFractionDigitsSet;
|
||||
|
||||
UnicodeString* fWindowsLocaleName; // Stores the equivalent Windows locale name.
|
||||
};
|
||||
|
||||
U_NAMESPACE_END
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue