diff --git a/icu-patches/patches/021-MSFT-Patch_ICU_Cldr2Icu_remove_icu4j_directory_validation.patch b/icu-patches/patches/021-MSFT-Patch_ICU_Cldr2Icu_remove_icu4j_directory_validation.patch new file mode 100644 index 00000000000..51be7eba985 --- /dev/null +++ b/icu-patches/patches/021-MSFT-Patch_ICU_Cldr2Icu_remove_icu4j_directory_validation.patch @@ -0,0 +1,20 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Arvind Oruganti +Date: Thu, 14 May 2026 14:30:00 +0530 +Subject: MSFT-PATCH: Remove icu4j directory validation from Cldr2Icu (icu4c-only fork) + +diff --git a/icu/tools/cldr/cldr-to-icu/src/main/java/org/unicode/icu/tool/cldrtoicu/Cldr2IcuCliOptions.java b/icu/tools/cldr/cldr-to-icu/src/main/java/org/unicode/icu/tool/cldrtoicu/Cldr2IcuCliOptions.java +index d9b46014438..9ff85b38d86 100644 +--- a/icu/tools/cldr/cldr-to-icu/src/main/java/org/unicode/icu/tool/cldrtoicu/Cldr2IcuCliOptions.java ++++ b/icu/tools/cldr/cldr-to-icu/src/main/java/org/unicode/icu/tool/cldrtoicu/Cldr2IcuCliOptions.java +@@ -380,7 +380,9 @@ private void validateEnvironment() { + + if (!new File(icuDir).isDirectory() + || ! new File(icuDir, "icu4c").isDirectory() +- || ! new File(icuDir, "icu4j").isDirectory() ++ // MSFT-Change: microsoft/icu fork is icu4c-only; no icu4j source tree. ++ // The Maven dependency on icu4j (used by TransformsMapper) is still ++ // resolved from ~/.m2 at runtime, so source-tree absence is harmless. + || ! new File(icuDir, "tools/cldr/cldr-to-icu").isDirectory() + || ! new File(icuDir, "tools/cldr/cldr-to-icu/pom.xml").isFile()) { + System.err.println("The `" + icuDir + "` directory does not look like a valid icu root."); diff --git a/icu/icu4c/source/common/common.vcxproj b/icu/icu4c/source/common/common.vcxproj index 81f7d0c20f9..aca55a102f9 100644 --- a/icu/icu4c/source/common/common.vcxproj +++ b/icu/icu4c/source/common/common.vcxproj @@ -41,7 +41,8 @@ $(OutDir)/icuuc.pch $(OutDir)/ $(OutDir)/ - $(OutDir)/icuuc.pdb + + $(OutDir)/icuuc$(IcuMajorVersion).pdb @@ -53,24 +54,34 @@ RBBI_DEBUG;%(PreprocessorDefinitions) - MultiThreadedDebugDLL + + MultiThreadedDebug ..\..\$(IcuBinOutputDir)\icuuc$(IcuMajorVersion)d.dll - .\..\..\$(IcuLibOutputDir)\icuucd.pdb + + .\..\..\$(IcuLibOutputDir)\icuuc$(IcuMajorVersion)d.pdb ..\..\$(IcuLibOutputDir)\icuucd.lib + + libucrtd.lib;libucrt.lib + /DEFAULTLIB:ucrtd.lib %(AdditionalOptions) - MultiThreadedDLL + + MultiThreaded true ..\..\$(IcuBinOutputDir)\icuuc$(IcuMajorVersion).dll - .\..\..\$(IcuLibOutputDir)\icuuc.pdb + + .\..\..\$(IcuLibOutputDir)\icuuc$(IcuMajorVersion).pdb ..\..\$(IcuLibOutputDir)\icuuc.lib + + libucrtd.lib;libucrt.lib + /DEFAULTLIB:ucrt.lib %(AdditionalOptions) @@ -275,6 +286,7 @@ + @@ -394,6 +406,7 @@ + diff --git a/icu/icu4c/source/common/common_uwp.vcxproj b/icu/icu4c/source/common/common_uwp.vcxproj index 01906f3caf9..113c2418902 100644 --- a/icu/icu4c/source/common/common_uwp.vcxproj +++ b/icu/icu4c/source/common/common_uwp.vcxproj @@ -409,6 +409,7 @@ + @@ -529,6 +530,7 @@ + diff --git a/icu/icu4c/source/common/putil.cpp b/icu/icu4c/source/common/putil.cpp index ea15fdff0b0..307b07d087d 100644 --- a/icu/icu4c/source/common/putil.cpp +++ b/icu/icu4c/source/common/putil.cpp @@ -66,6 +66,7 @@ #include "locmap.h" #include "ucln_cmn.h" #include "charstr.h" +#include "uprefs.h" /* Include standard headers. */ #include @@ -1794,10 +1795,37 @@ The leftmost codepage (.xxx) wins. return posixID; #elif U_PLATFORM_USES_ONLY_WIN32_API -#define POSIX_LOCALE_CAPACITY 64 UErrorCode status = U_ZERO_ERROR; char *correctedPOSIXLocale = nullptr; +#if UCONFIG_USE_WINDOWS_PREFERENCES_LIBRARY + + int32_t neededBufferSize = uprefs_getBCP47Tag(nullptr, 0, &status); + MaybeStackArray windowsLocale(neededBufferSize, status); + int32_t length = uprefs_getBCP47Tag(windowsLocale.getAlias(), neededBufferSize, &status); + + if (length > 0) // If length is 0, then the call to uprefs_getBCP47Tag failed. + { + // Now normalize the resulting name + correctedPOSIXLocale = static_cast(uprv_malloc(length * 2)); + /* TODO: Should we just exit on memory allocation failure? */ + if (correctedPOSIXLocale) + { + int32_t posixLen = uloc_canonicalize(windowsLocale.getAlias(), correctedPOSIXLocale, length * 2, &status); + if (U_SUCCESS(status)) + { + *(correctedPOSIXLocale + posixLen) = 0; + gCorrectedPOSIXLocale = correctedPOSIXLocale; + gCorrectedPOSIXLocaleHeapAllocated = true; + ucln_common_registerCleanup(UCLN_COMMON_PUTIL, putil_cleanup); + } + else + { + uprv_free(correctedPOSIXLocale); + } + } + } +#else // If we have already figured this out just use the cached value if (gCorrectedPOSIXLocale != nullptr) { return gCorrectedPOSIXLocale; @@ -1839,11 +1867,11 @@ The leftmost codepage (.xxx) wins. } // Now normalize the resulting name - correctedPOSIXLocale = static_cast(uprv_malloc(POSIX_LOCALE_CAPACITY + 1)); + correctedPOSIXLocale = static_cast(uprv_malloc(length * 2)); /* TODO: Should we just exit on memory allocation failure? */ if (correctedPOSIXLocale) { - int32_t posixLen = uloc_canonicalize(modifiedWindowsLocale, correctedPOSIXLocale, POSIX_LOCALE_CAPACITY, &status); + int32_t posixLen = uloc_canonicalize(modifiedWindowsLocale, correctedPOSIXLocale, length * 2, &status); if (U_SUCCESS(status)) { *(correctedPOSIXLocale + posixLen) = 0; @@ -1857,6 +1885,7 @@ The leftmost codepage (.xxx) wins. } } } +#endif // If unable to find a locale we can agree upon, use en-US by default if (gCorrectedPOSIXLocale == nullptr) { diff --git a/icu/icu4c/source/common/sources.txt b/icu/icu4c/source/common/sources.txt index 5b1c5e262ea..cfcfd95df56 100644 --- a/icu/icu4c/source/common/sources.txt +++ b/icu/icu4c/source/common/sources.txt @@ -163,6 +163,7 @@ unistr_titlecase_brkiter.cpp unorm.cpp unormcmp.cpp uobject.cpp +uprefs.cpp uprops.cpp ures_cnv.cpp uresbund.cpp diff --git a/icu/icu4c/source/common/ucln_cmn.cpp b/icu/icu4c/source/common/ucln_cmn.cpp index c63bd221929..6d7d2536381 100644 --- a/icu/icu4c/source/common/ucln_cmn.cpp +++ b/icu/icu4c/source/common/ucln_cmn.cpp @@ -36,8 +36,9 @@ static cleanupFunc *gLibCleanupFunctions[UCLN_COMMON]; The cleanup order is important in this function. Please be sure that you have read ucln.h ************************************************/ +// MSFT-Change: Make u_cleanup a no-op for the Windows OS ICU version. U_CAPI void U_EXPORT2 -u_cleanup() +uprv_u_cleanup() { UTRACE_ENTRY_OC(UTRACE_U_CLEANUP); icu::umtx_lock(nullptr); /* Force a memory barrier, so that we are sure to see */ @@ -52,6 +53,31 @@ u_cleanup() /*#endif*/ } +U_CAPI void U_EXPORT2 +u_cleanup() +{ +// When ICU is built as an OS component for Windows, we make the public function u_cleanup +// effectively a no-op because of the following: +// - It is not thread-safe and *forcefully* unloads the ICU data file. +// - The ICU library can simultaneously be used by other threads when this happens, +// either by Windows.Globalization, or by sorting functions when ICU sorting is default. +// - This means an App can call the function at any time, which will cause random crashes. :( +// +// We don't completely remove the functionality though, as we still want/need to be able to +// unload resources when the ICU DLL is unloaded. Instead we make it a private (uprv) function +// so that the combined DLL can still call it, and we don't export it in the DEF file. +// +// Note: We don't unconditionally do this though, as we don't want to alter the behavior of the +// public function when ICU when used/consumed in a Nuget package (for example). +// +#if defined(ICU_DATA_DIR_WINDOWS) + ((void)0); // no-op. + return; +#else + uprv_u_cleanup(); +#endif +} + U_CAPI void U_EXPORT2 ucln_cleanupOne(ECleanupLibraryType libType) { if (gLibCleanupFunctions[libType]) diff --git a/icu/icu4c/source/common/ucmndata.h b/icu/icu4c/source/common/ucmndata.h index 486b4fd7b5f..36f82f6f9ea 100644 --- a/icu/icu4c/source/common/ucmndata.h +++ b/icu/icu4c/source/common/ucmndata.h @@ -30,8 +30,12 @@ #include "unicode/udata.h" #include "umapfile.h" - -#define COMMON_DATA_NAME U_ICUDATA_NAME +// MSFT-Change: In the Windows OS ICU build, we only have one data package, and we use a versionless name in filename. +#if defined(ICU_DATA_DIR_WINDOWS) +# define COMMON_DATA_NAME "icudtl" +#else +# define COMMON_DATA_NAME U_ICUDATA_NAME +#endif typedef struct { uint16_t headerSize; diff --git a/icu/icu4c/source/common/udata.cpp b/icu/icu4c/source/common/udata.cpp index b9b737f177e..38a501719e3 100644 --- a/icu/icu4c/source/common/udata.cpp +++ b/icu/icu4c/source/common/udata.cpp @@ -809,6 +809,16 @@ openCommonData(const char *path, /* Path from OpenChoice? */ *----------------------------------------------------------------------*/ static UBool extendICUData(UErrorCode *pErr) { +// MSFT-Change: For the Windows OS build of ICU, we only have one data file +// and we don't use the extended data at all. We make this function a no-op +// in order to save a few cycles for perf, but more importantly so that +// we don't try to load a versioned data file (ex: icudt78l.dat) after +// already loading the non-versioned common data file. +#if defined(ICU_DATA_DIR_WINDOWS) + (void)pErr; // suppress unused variable. + return false; +#endif + UDataMemory *pData; UDataMemory copyPData; UBool didUpdate = false; diff --git a/icu/icu4c/source/common/unicode/putil.h b/icu/icu4c/source/common/unicode/putil.h index 500c21252fc..c0b34556ebc 100644 --- a/icu/icu4c/source/common/unicode/putil.h +++ b/icu/icu4c/source/common/unicode/putil.h @@ -42,6 +42,9 @@ * functions may have to be re-implemented. */ +//IGNORE_WINDOWS_HEADERS_START +// MSFT-Change: The Windows OS version of ICU uses a single fixed data file. + /** * Return the ICU data directory. * The data directory is where common format ICU data files (.dat files) @@ -112,7 +115,7 @@ U_CAPI const char * U_EXPORT2 u_getTimeZoneFilesDirectory(UErrorCode *status); U_CAPI void U_EXPORT2 u_setTimeZoneFilesDirectory(const char *path, UErrorCode *status); #endif /* U_HIDE_INTERNAL_API */ - +// MSFT-TODO: Should these be considered for Windows? /** * @{ * Filesystem file and path separator characters. @@ -134,6 +137,7 @@ U_CAPI void U_EXPORT2 u_setTimeZoneFilesDirectory(const char *path, UErrorCode * # define U_FILE_ALT_SEP_STRING "/" # define U_PATH_SEP_STRING ":" #endif +//IGNORE_WINDOWS_HEADERS_END /** @} */ diff --git a/icu/icu4c/source/common/unicode/uchar.h b/icu/icu4c/source/common/unicode/uchar.h index d33b8cf7f3c..047d61c8122 100644 --- a/icu/icu4c/source/common/unicode/uchar.h +++ b/icu/icu4c/source/common/unicode/uchar.h @@ -49,6 +49,10 @@ typedef struct USet USet; U_CDECL_BEGIN +//IGNORE_WINDOWS_HEADERS_START +// MSFT-Change: The value of these macros can change at runtime, so the API u_getUnicodeVersion +// should be used instead of any version macro. + /*==========================================================================*/ /* Unicode version number */ /*==========================================================================*/ @@ -63,6 +67,8 @@ U_CDECL_BEGIN */ #define U_UNICODE_VERSION "17.0" +//IGNORE_WINDOWS_HEADERS_END + /** * \file * \brief C API: Unicode Properties diff --git a/icu/icu4c/source/common/unicode/uconfig.h b/icu/icu4c/source/common/unicode/uconfig.h index c0488d502bf..f339dec982f 100644 --- a/icu/icu4c/source/common/unicode/uconfig.h +++ b/icu/icu4c/source/common/unicode/uconfig.h @@ -45,6 +45,10 @@ * @stable ICU 2.4 */ +//IGNORE_WINDOWS_HEADERS_START +// MSFT-Change: Since these are compile time settings, it doesn't make sense to +// load a user config header in the Windows OS SDK version. + /** * If this switch is defined, ICU will attempt to load a header file named "uconfig_local.h" * prior to determining default settings for uconfig variables. @@ -55,6 +59,8 @@ #include "uconfig_local.h" #endif +//IGNORE_WINDOWS_HEADERS_END + /** * \def U_DEBUG * Determines whether to include debugging code. @@ -379,6 +385,9 @@ # define UCONFIG_MSGPAT_DEFAULT_APOSTROPHE_MODE UMSGPAT_APOS_DOUBLE_OPTIONAL #endif +//IGNORE_WINDOWS_HEADERS_START +// MSFT-Change: We always use the OS LCID mapping API for the Windows OS build of ICU. + /** * \def UCONFIG_USE_WINDOWS_LCID_MAPPING_API * On platforms where U_PLATFORM_HAS_WIN32_API is true, this switch determines @@ -391,6 +400,24 @@ # define UCONFIG_USE_WINDOWS_LCID_MAPPING_API 1 #endif +/** + * \def UCONFIG_USE_WINDOWS_PREFERENCES_LIBRARY + * On Windows platforms (ie: U_PLATFORM_HAS_WIN32_API is true), this switch enables ICU to + * detect additional user preferences by setting BCP47 Unicode extension within the default locale. + * This includes information such as calendar, currency, hour cycle, among others. + * + * If this switch is off (or set to 0) then the default behavior of only detecting the language + * and country/region occurs. + * + * For example, the default locale may be detected as "es-MX-u-hc-h24", instead of "es-MX", + * if the user has selected a 24 hour clock option. +*/ +#ifndef UCONFIG_USE_WINDOWS_PREFERENCES_LIBRARY +# define UCONFIG_USE_WINDOWS_PREFERENCES_LIBRARY 1 +#endif + +//IGNORE_WINDOWS_HEADERS_END + /* i18n library switches ---------------------------------------------------- */ /** diff --git a/icu/icu4c/source/common/unicode/unistr.h b/icu/icu4c/source/common/unicode/unistr.h index 161b84527a6..145bb8ea3ef 100644 --- a/icu/icu4c/source/common/unicode/unistr.h +++ b/icu/icu4c/source/common/unicode/unistr.h @@ -60,6 +60,9 @@ class Edits; U_NAMESPACE_END +//IGNORE_WINDOWS_HEADERS_START +// MSFT-Change: Hiding the @internal API below, since we don't expose the C++ UnicodeString. + // Not #ifndef U_HIDE_INTERNAL_API because UnicodeString needs the UStringCaseMapper. /** * Internal string case mapping function type. @@ -77,6 +80,8 @@ UStringCaseMapper(int32_t caseLocale, uint32_t options, icu::Edits *edits, UErrorCode &errorCode); +//IGNORE_WINDOWS_HEADERS_END + U_NAMESPACE_BEGIN class Locale; // unicode/locid.h diff --git a/icu/icu4c/source/common/unicode/utypes.h b/icu/icu4c/source/common/unicode/utypes.h index f53b7536f20..0f6bc70fd54 100644 --- a/icu/icu4c/source/common/unicode/utypes.h +++ b/icu/icu4c/source/common/unicode/utypes.h @@ -104,6 +104,11 @@ /** @} */ +//IGNORE_WINDOWS_HEADERS_START +// MSFT-Change: For the Windows OS version of ICU, it doesn't make sense to expose these +// constants which are only for loading the main ICU data file. We also don't +// support using a data DLL either, so omit them from the Windows SDK header. + /*===========================================================================*/ /* ICUDATA naming scheme */ /*===========================================================================*/ @@ -191,6 +196,8 @@ #endif #endif /* U_HIDE_INTERNAL_API */ +//IGNORE_WINDOWS_HEADERS_END + /** * \def NULL * Define NULL if necessary, to nullptr for C++ and to ((void *)0) for C. diff --git a/icu/icu4c/source/common/unicode/uversion.h b/icu/icu4c/source/common/unicode/uversion.h index 450794ac1ba..dc5ed687cf5 100644 --- a/icu/icu4c/source/common/unicode/uversion.h +++ b/icu/icu4c/source/common/unicode/uversion.h @@ -58,6 +58,10 @@ */ typedef uint8_t UVersionInfo[U_MAX_VERSION_LENGTH]; +//IGNORE_WINDOWS_HEADERS_START +// MSFT-Change: Since the Windows OS ICU headers are for C APIs only, we don't +// need or want any C++ namespace support. + /*===========================================================================*/ /* C++ namespace if supported. Versioned unless versioning is disabled. */ /*===========================================================================*/ @@ -184,6 +188,8 @@ namespace U_HEADER_ONLY_NAMESPACE {} #endif /* __cplusplus */ +//IGNORE_WINDOWS_HEADERS_END + /*===========================================================================*/ /* General version helper functions. Definitions in putil.c */ /*===========================================================================*/ diff --git a/icu/icu4c/source/common/uprefs.cpp b/icu/icu4c/source/common/uprefs.cpp new file mode 100644 index 00000000000..d9304a11de0 --- /dev/null +++ b/icu/icu4c/source/common/uprefs.cpp @@ -0,0 +1,553 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#include "uprefs.h" +#if U_PLATFORM_USES_ONLY_WIN32_API && UCONFIG_USE_WINDOWS_PREFERENCES_LIBRARY +#include "unicode/ustring.h" +#include "cmemory.h" +#include "charstr.h" +#include "cstring.h" +#include "cwchar.h" +#include + +U_NAMESPACE_USE + +// Older versions of the Windows SDK don’t have definitions for calendar types that were added later on. +// (For example, the Windows 7 SDK doesn’t have CAL_PERSIAN). +// So we’ll need to provide our own definitions for some of them. +// Note that on older versions of the OS these values won't ever be returned by the platform APIs, so providing our own definitions is fine. +#ifndef CAL_PERSIAN +#define CAL_PERSIAN 22 // Persian (Solar Hijri) calendar +#endif + +#define RETURN_FAILURE_STRING_WITH_STATUS_IF(condition, error, status) \ + if (condition) \ + { \ + *status = error; \ + return CharString(); \ + } + +#define RETURN_FAILURE_WITH_STATUS_IF(condition, error, status) \ + if (condition) \ + { \ + *status = error; \ + return 0; \ + } + +#define RETURN_VALUE_IF(condition, value) \ + if (condition) \ + { \ + return value; \ + } + +#define RETURN_WITH_ALLOCATION_ERROR_IF_FAILED(status) \ + if (U_FAILURE(*status)) \ + { \ + *status = U_MEMORY_ALLOCATION_ERROR; \ + return CharString(); \ + } \ +// ------------------------------------------------------- +// ----------------- MAPPING FUNCTIONS-------------------- +// ------------------------------------------------------- + +// Maps from a NLS Calendar ID (CALID) to a BCP47 Unicode Extension calendar identifier. +// +// We map the NLS CALID from GetLocaleInfoEx to the calendar identifier +// used in BCP47 tag with Unicode Extensions. +// +// This does not return a full nor valid BCP47Tag, it only returns the option that the BCP47 tag +// would return after the "ca-" part +// +// For example: +// CAL_GREGORIAN would return "gregory". +// CAL_HIJRI would return "islamic". +// +// These could be used in a BCP47 tag like this: "en-US-u-ca-gregory". +// Note that there are some NLS calendars that are not supported with the BCP47 U extensions, +// and vice-versa. +// +// NLS CALID reference:https://docs.microsoft.com/en-us/windows/win32/intl/calendar-identifiers +CharString getCalendarBCP47FromNLSType(int32_t calendar, UErrorCode* status) +{ + switch(calendar) + { + case CAL_GREGORIAN: + case CAL_GREGORIAN_US: + case CAL_GREGORIAN_ME_FRENCH: + case CAL_GREGORIAN_ARABIC: + case CAL_GREGORIAN_XLIT_ENGLISH: + case CAL_GREGORIAN_XLIT_FRENCH: + return CharString("gregory", *status); + + case CAL_JAPAN: + return CharString("japanese", *status); + + case CAL_TAIWAN: + return CharString("roc", *status); + + case CAL_KOREA: + return CharString("dangi", *status); + + case CAL_HIJRI: + return CharString("islamic", *status); + + case CAL_THAI: + return CharString("buddhist", *status); + + case CAL_HEBREW: + return CharString("hebrew", *status); + + case CAL_PERSIAN: + return CharString("persian", *status); + + case CAL_UMALQURA: + return CharString("islamic-umalqura", *status); + + default: + return CharString(); + } +} + +// Maps from a NLS Alternate sorting system to a BCP47 U extension sorting system. +// +// We map the alternate sorting method from GetLocaleInfoEx to the sorting method +// used in BCP47 tag with Unicode Extensions. +// +// This does not return a full nor valid BCP47Tag, it only returns the option that the BCP47 tag +// would return after the "co-" part +// +// For example: +// "phoneb" (parsed from "de-DE_phoneb") would return "phonebk". +// "radstr" (parsed from "ja-JP_radstr") would return "unihan". +// +// These could be used in a BCP47 tag like this: "de-DE-u-co-phonebk". +// Note that there are some NLS Alternate sort methods that are not supported with the BCP47 U extensions, +// and vice-versa. +CharString getSortingSystemBCP47FromNLSType(const wchar_t* sortingSystem, UErrorCode* status) +{ + if (wcscmp(sortingSystem, L"phoneb") == 0) // Phonebook style ordering (such as in German) + { + return CharString("phonebk", *status); + } + else if (wcscmp(sortingSystem, L"tradnl") == 0) // Traditional style ordering (such as in Spanish) + { + return CharString("trad", *status); + } + else if (wcscmp(sortingSystem, L"stroke") == 0) // Pinyin ordering for Latin, stroke order for CJK characters (used in Chinese) + { + return CharString("stroke", *status); + } + else if (wcscmp(sortingSystem, L"radstr") == 0) // Pinyin ordering for Latin, Unihan radical-stroke ordering for CJK characters (used in Chinese) + { + return CharString("unihan", *status); + } + else if (wcscmp(sortingSystem, L"pronun") == 0) // Phonetic ordering (sorting based on pronunciation) + { + return CharString("phonetic", *status); + } + else + { + return CharString(); + } +} + +// Maps from a NLS first day of week value to a BCP47 U extension first day of week. +// +// NLS defines: +// 0 -> Monday, 1 -> Tuesday, ... 5 -> Saturday, 6 -> Sunday +// +// We map the first day of week from GetLocaleInfoEx to the first day of week +// used in BCP47 tag with Unicode Extensions. +// +// This does not return a full nor valid BCP47Tag, it only returns the option that the BCP47 tag +// would return after the "fw-" part +// +// For example: +// 1 (Tuesday) would return "tue". +// 6 (Sunday) would return "sun". +// +// These could be used in a BCP47 tag like this: "en-US-u-fw-sun". +CharString getFirstDayBCP47FromNLSType(int32_t firstday, UErrorCode* status) +{ + switch(firstday) + { + case 0: + return CharString("mon", *status); + + case 1: + return CharString("tue", *status); + + case 2: + return CharString("wed", *status); + + case 3: + return CharString("thu", *status); + + case 4: + return CharString("fri", *status); + + case 5: + return CharString("sat", *status); + + case 6: + return CharString("sun", *status); + + default: + return CharString(); + } +} + +// Maps from a NLS Measurement system to a BCP47 U extension measurement system. +// +// NLS defines: +// 0 -> Metric system, 1 -> U.S. System +// +// This does not return a full nor valid BCP47Tag, it only returns the option that the BCP47 tag +// would return after the "ms-" part +// +// For example: +// 0 (Metric) would return "metric". +// 6 (U.S. System) would return "ussystem". +// +// These could be used in a BCP47 tag like this: "en-US-u-ms-metric". +CharString getMeasureSystemBCP47FromNLSType(int32_t measureSystem, UErrorCode *status) +{ + switch(measureSystem) + { + case 0: + return CharString("metric", *status); + case 1: + return CharString("ussystem", *status); + default: + return CharString(); + } +} + +// ------------------------------------------------------- +// --------------- END OF MAPPING FUNCTIONS -------------- +// ------------------------------------------------------- + +// ------------------------------------------------------- +// ------------------ HELPER FUCTIONS ------------------- +// ------------------------------------------------------- + +// Return the CLDR "h12" or "h23" format for the 12 or 24 hour clock. +// NLS only gives us a "time format" of a form similar to "h:mm:ss tt" +// The NLS "h" is 12 hour, and "H" is 24 hour, so we'll scan for the +// first h or H. +// Note that the NLS string could have sections escaped with single +// quotes, so be sure to skip those parts. Eg: "'Hours:' h:mm:ss" +// would skip the "H" in 'Hours' and use the h in the actual pattern. +CharString get12_or_24hourFormat(wchar_t* hourFormat, UErrorCode* status) +{ + bool isInEscapedString = false; + const int32_t hourLength = static_cast(uprv_wcslen(hourFormat)); + for (int32_t i = 0; i < hourLength; i++) + { + // Toggle escaped flag if in ' quoted portion + if (hourFormat[i] == L'\'') + { + isInEscapedString = !isInEscapedString; + } + + if (!isInEscapedString) + { + // Check for both so we can escape early + if (hourFormat[i] == L'H') + { + return CharString("h23", *status); + } + + if (hourFormat[i] == L'h') + { + return CharString("h12", *status); + } + } + } + // default to a 24 hour clock as that's more common worldwide + return CharString("h23", *status); +} + +UErrorCode getUErrorCodeFromLastError() +{ + DWORD error = GetLastError(); + switch(error) + { + case ERROR_INSUFFICIENT_BUFFER: + return U_BUFFER_OVERFLOW_ERROR; + + case ERROR_INVALID_FLAGS: + case ERROR_INVALID_PARAMETER: + return U_ILLEGAL_ARGUMENT_ERROR; + + case ERROR_OUTOFMEMORY: + return U_MEMORY_ALLOCATION_ERROR; + + default: + return U_INTERNAL_PROGRAM_ERROR; + } +} + +int32_t GetLocaleInfoExWrapper(LPCWSTR lpLocaleName, LCTYPE LCType, LPWSTR lpLCData, int cchData, UErrorCode* status) +{ + RETURN_VALUE_IF(U_FAILURE(*status), 0); + +#ifndef UPREFS_TEST + *status = U_ZERO_ERROR; + int32_t result = GetLocaleInfoEx(lpLocaleName, LCType, lpLCData, cchData); + + if (result == 0) + { + *status = getUErrorCodeFromLastError(); + } + return result; +#else + #include "uprefstest.h" + UPrefsTest prefTests; + return prefTests.MockGetLocaleInfoEx(lpLocaleName, LCType, lpLCData, cchData, status); +#endif +} + +// Copies a string to a buffer if its size allows it and returns the size. +// The returned needed buffer size includes the terminating \0 null character. +// If the buffer's size is set to 0, the needed buffer size is returned before copying the string. +int32_t checkBufferCapacityAndCopy(const char* uprefsString, char* uprefsBuffer, int32_t bufferSize, UErrorCode* status) +{ + int32_t neededBufferSize = static_cast(uprv_strlen(uprefsString) + 1); + + RETURN_VALUE_IF(bufferSize == 0, neededBufferSize); + RETURN_FAILURE_WITH_STATUS_IF(neededBufferSize > bufferSize, U_BUFFER_OVERFLOW_ERROR, status); + + uprv_strcpy(uprefsBuffer, uprefsString); + + return neededBufferSize; +} + +CharString getLocaleBCP47Tag_impl(UErrorCode* status, bool getSorting) +{ + // First part of a bcp47 tag looks like an NLS user locale, so we get the NLS user locale. + int32_t neededBufferSize = GetLocaleInfoExWrapper(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, nullptr, 0, status); + + RETURN_VALUE_IF(U_FAILURE(*status), CharString()); + + MaybeStackArray NLSLocale(neededBufferSize, *status); + RETURN_WITH_ALLOCATION_ERROR_IF_FAILED(status); + + int32_t result = GetLocaleInfoExWrapper(LOCALE_NAME_USER_DEFAULT, LOCALE_SNAME, NLSLocale.getAlias(), neededBufferSize, status); + + RETURN_VALUE_IF(U_FAILURE(*status), CharString()); + + if (getSorting) //We determine if we want the locale (for example, en-US) or the sorting method (for example, phonebk) + { + // We use LOCALE_SNAME to get the sorting method (if any). So we need to keep + // only the sorting bit after the _, removing the locale name. + // Example: from "de-DE_phoneb" we only want "phoneb" + const wchar_t * startPosition = wcschr(NLSLocale.getAlias(), L'_'); + + // Note: not finding a "_" is not an error, it means the user has not selected an alternate sorting method, which is fine. + if (startPosition != nullptr) + { + CharString sortingSystem = getSortingSystemBCP47FromNLSType(startPosition + 1, status); + + if (sortingSystem.length() == 0) + { + *status = U_UNSUPPORTED_ERROR; + return CharString(); + } + return sortingSystem; + } + } + else + { + // The NLS locale may include a non-default sort, such as de-DE_phoneb. We only want the locale name before the _. + wchar_t * position = wcschr(NLSLocale.getAlias(), L'_'); + if (position != nullptr) + { + *position = L'\0'; + } + + CharString languageTag; + int32_t resultCapacity = 0; + languageTag.getAppendBuffer(neededBufferSize, neededBufferSize, resultCapacity, *status); + RETURN_WITH_ALLOCATION_ERROR_IF_FAILED(status); + + int32_t unitsWritten = 0; + u_strToUTF8(languageTag.data(), neededBufferSize, &unitsWritten, reinterpret_cast(NLSLocale.getAlias()), neededBufferSize, status); + RETURN_VALUE_IF(U_FAILURE(*status), CharString()); + return languageTag; + } + + return CharString(); +} + +CharString getCalendarSystem_impl(UErrorCode* status) +{ + int32_t NLSCalendar = 0; + + GetLocaleInfoExWrapper(LOCALE_NAME_USER_DEFAULT, LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER, reinterpret_cast(&NLSCalendar), sizeof(NLSCalendar) / sizeof(wchar_t), status); + + RETURN_VALUE_IF(U_FAILURE(*status), CharString()); + + CharString calendar(getCalendarBCP47FromNLSType(NLSCalendar, status), *status); + RETURN_FAILURE_STRING_WITH_STATUS_IF(calendar.length() == 0, U_UNSUPPORTED_ERROR, status); + + return calendar; +} + +CharString getCurrencyCode_impl(UErrorCode* status) +{ + int32_t neededBufferSize = GetLocaleInfoExWrapper(LOCALE_NAME_USER_DEFAULT, LOCALE_SINTLSYMBOL, nullptr, 0, status); + + RETURN_VALUE_IF(U_FAILURE(*status), CharString()); + + MaybeStackArray NLScurrencyData(neededBufferSize, *status); + RETURN_WITH_ALLOCATION_ERROR_IF_FAILED(status); + + int32_t result = GetLocaleInfoExWrapper(LOCALE_NAME_USER_DEFAULT, LOCALE_SINTLSYMBOL, NLScurrencyData.getAlias(), neededBufferSize, status); + + RETURN_VALUE_IF(U_FAILURE(*status), CharString()); + + MaybeStackArray currency(neededBufferSize, *status); + RETURN_WITH_ALLOCATION_ERROR_IF_FAILED(status); + + int32_t unitsWritten = 0; + u_strToUTF8(currency.getAlias(), neededBufferSize, &unitsWritten, reinterpret_cast(NLScurrencyData.getAlias()), neededBufferSize, status); + RETURN_VALUE_IF(U_FAILURE(*status), CharString()); + + if (unitsWritten == 0) + { + *status = U_INTERNAL_PROGRAM_ERROR; + return CharString(); + } + + // Since we retreived the currency code in caps, we need to make it lowercase for it to be in CLDR BCP47 U extensions format. + T_CString_toLowerCase(currency.getAlias()); + + return CharString(currency.getAlias(), neededBufferSize, *status); +} + +CharString getFirstDayOfWeek_impl(UErrorCode* status) +{ + int32_t NLSfirstDay = 0; + GetLocaleInfoExWrapper(LOCALE_NAME_USER_DEFAULT, LOCALE_IFIRSTDAYOFWEEK | LOCALE_RETURN_NUMBER, reinterpret_cast(&NLSfirstDay), sizeof(NLSfirstDay) / sizeof(wchar_t), status); + + RETURN_VALUE_IF(U_FAILURE(*status), CharString()); + + CharString firstDay = getFirstDayBCP47FromNLSType(NLSfirstDay, status); + RETURN_FAILURE_STRING_WITH_STATUS_IF(firstDay.length() == 0, U_UNSUPPORTED_ERROR, status); + + return firstDay; +} + +CharString getHourCycle_impl(UErrorCode* status) +{ + int32_t neededBufferSize = GetLocaleInfoExWrapper(LOCALE_NAME_USER_DEFAULT, LOCALE_STIMEFORMAT, nullptr, 0, status); + + RETURN_VALUE_IF(U_FAILURE(*status), CharString()); + + MaybeStackArray NLShourCycle(neededBufferSize, *status); + RETURN_WITH_ALLOCATION_ERROR_IF_FAILED(status); + + int32_t result = GetLocaleInfoExWrapper(LOCALE_NAME_USER_DEFAULT, LOCALE_STIMEFORMAT, NLShourCycle.getAlias(), neededBufferSize, status); + + RETURN_VALUE_IF(U_FAILURE(*status), CharString()); + + CharString hourCycle = get12_or_24hourFormat(NLShourCycle.getAlias(), status); + if (hourCycle.length() == 0) + { + *status = U_INTERNAL_PROGRAM_ERROR; + return CharString(); + } + return hourCycle; +} + +CharString getMeasureSystem_impl(UErrorCode* status) +{ + int32_t NLSmeasureSystem = 0; + GetLocaleInfoExWrapper(LOCALE_NAME_USER_DEFAULT, LOCALE_IMEASURE | LOCALE_RETURN_NUMBER, reinterpret_cast(&NLSmeasureSystem), sizeof(NLSmeasureSystem) / sizeof(wchar_t), status); + + RETURN_VALUE_IF(U_FAILURE(*status), CharString()); + + CharString measureSystem = getMeasureSystemBCP47FromNLSType(NLSmeasureSystem, status); + RETURN_FAILURE_STRING_WITH_STATUS_IF(measureSystem.length() == 0, U_UNSUPPORTED_ERROR, status); + + return measureSystem; +} + +void appendIfDataNotEmpty(CharString& dest, const char* firstData, const char* secondData, bool& warningGenerated, UErrorCode* status) +{ + if (*status == U_UNSUPPORTED_ERROR) + { + warningGenerated = true; + *status = U_ZERO_ERROR; + } + + if (uprv_strlen(secondData) != 0) + { + dest.append(firstData, *status); + dest.append(secondData, *status); + } +} +// ------------------------------------------------------- +// --------------- END OF HELPER FUNCTIONS --------------- +// ------------------------------------------------------- + + +// ------------------------------------------------------- +// ---------------------- APIs --------------------------- +// ------------------------------------------------------- + +// Gets the valid and canonical BCP47 tag with the user settings for Language, Calendar, Sorting, Currency, +// First day of week, Hour cycle, and Measurement system. +// Calls all of the other APIs +// Returns the needed buffer size for the BCP47 Tag. +int32_t uprefs_getBCP47Tag(char* uprefsBuffer, int32_t bufferSize, UErrorCode* status) +{ + RETURN_FAILURE_WITH_STATUS_IF(uprefsBuffer == nullptr && bufferSize != 0, U_ILLEGAL_ARGUMENT_ERROR, status); + + *status = U_ZERO_ERROR; + CharString BCP47Tag; + bool warningGenerated = false; + + CharString languageTag = getLocaleBCP47Tag_impl(status, false); + RETURN_VALUE_IF(U_FAILURE(*status), 0); + BCP47Tag.append(languageTag.data(), *status); + BCP47Tag.append("-u", *status); + + CharString calendar = getCalendarSystem_impl(status); + RETURN_VALUE_IF(U_FAILURE(*status) && *status != U_UNSUPPORTED_ERROR, 0); + appendIfDataNotEmpty(BCP47Tag, "-ca-", calendar.data(), warningGenerated, status); + + CharString sortingSystem = getLocaleBCP47Tag_impl(status, true); + RETURN_VALUE_IF(U_FAILURE(*status) && *status != U_UNSUPPORTED_ERROR, 0); + appendIfDataNotEmpty(BCP47Tag, "-co-", sortingSystem.data(), warningGenerated, status); + + CharString currency = getCurrencyCode_impl(status); + RETURN_VALUE_IF(U_FAILURE(*status) && *status != U_UNSUPPORTED_ERROR, 0); + appendIfDataNotEmpty(BCP47Tag, "-cu-", currency.data(), warningGenerated, status); + + CharString firstDay = getFirstDayOfWeek_impl(status); + RETURN_VALUE_IF(U_FAILURE(*status) && *status != U_UNSUPPORTED_ERROR, 0); + appendIfDataNotEmpty(BCP47Tag, "-fw-", firstDay.data(), warningGenerated, status); + + CharString hourCycle = getHourCycle_impl(status); + RETURN_VALUE_IF(U_FAILURE(*status) && *status != U_UNSUPPORTED_ERROR, 0); + appendIfDataNotEmpty(BCP47Tag, "-hc-", hourCycle.data(), warningGenerated, status); + + CharString measureSystem = getMeasureSystem_impl(status); + RETURN_VALUE_IF(U_FAILURE(*status) && *status != U_UNSUPPORTED_ERROR, 0); + appendIfDataNotEmpty(BCP47Tag, "-ms-", measureSystem.data(), warningGenerated, status); + + if (warningGenerated) + { + *status = U_USING_FALLBACK_WARNING; + } + + return checkBufferCapacityAndCopy(BCP47Tag.data(), uprefsBuffer, bufferSize, status); +} + +// ------------------------------------------------------- +// ---------------------- END OF APIs -------------------- +// ------------------------------------------------------- + +#endif // U_PLATFORM_USES_ONLY_WIN32_API && UCONFIG_USE_WINDOWS_PREFERENCES_LIBRARY \ No newline at end of file diff --git a/icu/icu4c/source/common/uprefs.h b/icu/icu4c/source/common/uprefs.h new file mode 100644 index 00000000000..08ecd86189b --- /dev/null +++ b/icu/icu4c/source/common/uprefs.h @@ -0,0 +1,29 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html + +#ifndef UPREFS_H +#define UPREFS_H + +#include "unicode/platform.h" +#include "unicode/utypes.h" +#if U_PLATFORM_USES_ONLY_WIN32_API && UCONFIG_USE_WINDOWS_PREFERENCES_LIBRARY + +/** +* Gets the valid and canonical BCP47 tag with the user settings for Language, Calendar, Sorting, Currency, +* First day of week, Hour cycle, and Measurement system when available. +* +* @param uprefsBuffer Pointer to a buffer in which this function retrieves the BCP47 tag. +* This pointer is not used if bufferSize is set to 0. +* @param bufferSize Size, in characters, of the data buffer indicated by uprefsBuffer. Alternatively, the application +* can set this parameter to 0. In this case, the function does not use the uprefsBuffer parameter +* and returns the required buffer size, including the terminating null character. +* @param status: Pointer to a UErrorCode. The resulting value will be U_ZERO_ERROR if the call was successful or will +* contain an error or warning code. If the status is U_USING_FALLBACK_WARNING, it means at least one of the + settings was not succesfully mapped between NLS and CLDR, so it will not be shown on the BCP47 tag. +* @return The needed buffer size, including the terminating \0 null character if the call was successful, should be ignored +* if status was not U_ZERO_ERROR. +*/ +int32_t uprefs_getBCP47Tag(char* uprefsBuffer, int32_t bufferSize, UErrorCode* status); + +#endif //U_PLATFORM_USES_ONLY_WIN32_API && UCONFIG_USE_WINDOWS_PREFERENCES_LIBRARY +#endif //UPREFS_H \ No newline at end of file diff --git a/icu/icu4c/source/config/dist.mk b/icu/icu4c/source/config/dist.mk index a8bda27d8cd..319470143c6 100644 --- a/icu/icu4c/source/config/dist.mk +++ b/icu/icu4c/source/config/dist.mk @@ -37,7 +37,9 @@ DISTY_DATA_ZIP=$(DISTY_FILE_DIR)/$(DISTY_PREFIX)-data.zip DISTY_DAT:=$(firstword $(wildcard data/out/tmp/icudt$(SO_TARGET_VERSION_MAJOR)*.dat)) DISTY_FILES_SRC=$(DISTY_FILE_TGZ) $(DISTY_FILE_ZIP) -DISTY_FILES=$(DISTY_FILES_SRC) $(DISTY_DOC_ZIP) +# MSFT-Change: We only want the tgz for now, as we don't currently build the docs. +#DISTY_FILES=$(DISTY_FILES_SRC) $(DISTY_DOC_ZIP) +DISTY_FILES=$(DISTY_FILES_SRC) # colon-equals because we want to run this once! EXCLUDES_FILE:=$(shell mktemp) @@ -66,7 +68,8 @@ $(DISTY_FILE_TGZ) $(DISTY_FILE_ZIP) $(DISTY_DATA_ZIP): $(DISTY_DAT) $(DISTY_TMP @echo Export icu4c@$(GITVER) to "$(DISTY_TMP)/icu" -$(RMV) $(DISTY_FILE) $(DISTY_TMP) $(MKINSTALLDIRS) $(DISTY_TMP) - ( cd $(ICU4CTOP)/.. && git archive --format=tar --prefix=icu/ HEAD:icu4c/ ) | ( cd "$(DISTY_TMP)" && tar xf - ) + # MSFT-Change: Adjust the path for the GitHub repo location. + ( cd $(ICU4CTOP)/../.. && git archive --format=tar --prefix=icu/ HEAD:icu/icu4c/ ) | ( cd "$(DISTY_TMP)" && tar xf - ) # special handling for LICENSE file. The symlinks will be included as files by tar and zip. cp -fv $(ICU4CTOP)/LICENSE "$(DISTY_TMP)/LICENSE" # Copy top-level testdata directory so it's a sibling of the source/ directory diff --git a/icu/icu4c/source/data/build.xml b/icu/icu4c/source/data/build.xml index a7efc0675b1..3e3c0425703 100644 --- a/icu/icu4c/source/data/build.xml +++ b/icu/icu4c/source/data/build.xml @@ -43,7 +43,8 @@ - + + diff --git a/icu/icu4c/source/data/curr/ar_SA.txt b/icu/icu4c/source/data/curr/ar_SA.txt index 32526c8458f..2d969daeb3b 100644 --- a/icu/icu4c/source/data/curr/ar_SA.txt +++ b/icu/icu4c/source/data/curr/ar_SA.txt @@ -1,9 +1,11 @@ // © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html // Generated using tools/cldr/cldr-to-icu/ -/** - * generated alias target - */ ar_SA{ - ___{""} + Currencies{ + SAR{ + "⃁", + "ريال سعودي", + } + } } diff --git a/icu/icu4c/source/i18n/i18n.vcxproj b/icu/icu4c/source/i18n/i18n.vcxproj index 9803e1d1d5b..2a674773021 100644 --- a/icu/icu4c/source/i18n/i18n.vcxproj +++ b/icu/icu4c/source/i18n/i18n.vcxproj @@ -42,7 +42,8 @@ $(OutDir)/icuin.pch $(OutDir)/ $(OutDir)/ - $(OutDir)/icuin.pdb + + $(OutDir)/icuin$(IcuMajorVersion).pdb ../common;%(AdditionalIncludeDirectories) @@ -54,26 +55,36 @@ - MultiThreadedDebugDLL + + MultiThreadedDebug icuucd.lib;%(AdditionalDependencies) ..\..\$(IcuBinOutputDir)\icuin$(IcuMajorVersion)d.dll - .\..\..\$(IcuLibOutputDir)\icuind.pdb + + .\..\..\$(IcuLibOutputDir)\icuin$(IcuMajorVersion)d.pdb ..\..\$(IcuLibOutputDir)\icuind.lib + + libucrtd.lib;libucrt.lib + /DEFAULTLIB:ucrtd.lib %(AdditionalOptions) - MultiThreadedDLL + + MultiThreaded true icuuc.lib;%(AdditionalDependencies) ..\..\$(IcuBinOutputDir)\icuin$(IcuMajorVersion).dll - .\..\..\$(IcuLibOutputDir)\icuin.pdb + + .\..\..\$(IcuLibOutputDir)\icuin$(IcuMajorVersion).pdb ..\..\$(IcuLibOutputDir)\icuin.lib + + libucrtd.lib;libucrt.lib + /DEFAULTLIB:ucrt.lib %(AdditionalOptions) diff --git a/icu/icu4c/source/stubdata/stubdata.vcxproj b/icu/icu4c/source/stubdata/stubdata.vcxproj index 0b2c61cb06b..9c147a57a14 100644 --- a/icu/icu4c/source/stubdata/stubdata.vcxproj +++ b/icu/icu4c/source/stubdata/stubdata.vcxproj @@ -42,7 +42,8 @@ $(OutDir)/icudt.pch $(OutDir)/ $(OutDir)/ - $(OutDir)/icudt.pdb + + $(OutDir)/icudt$(IcuMajorVersion).pdb STUBDATA_BUILD;%(PreprocessorDefinitions) @@ -56,7 +57,8 @@ true ..\..\$(IcuBinOutputDir)\icudt$(IcuMajorVersion).dll - .\..\..\$(IcuLibOutputDir)\icudt.pdb + + .\..\..\$(IcuLibOutputDir)\icudt$(IcuMajorVersion).pdb ..\..\$(IcuLibOutputDir)\icudt.lib diff --git a/icu/icu4c/source/test/cintltst/currtest.c b/icu/icu4c/source/test/cintltst/currtest.c index d8355133a18..51dff0eb0b3 100644 --- a/icu/icu4c/source/test/cintltst/currtest.c +++ b/icu/icu4c/source/test/cintltst/currtest.c @@ -298,6 +298,26 @@ static void TestNumericCode(void) { } } +static void TestSaudiRiyalSymbol(void) { + UErrorCode status = U_ZERO_ERROR; + UChar currency[4]; + UBool isChoiceFormat = false; + int32_t len = 0; + static const UChar expectedSymbol[] = {0x20C1, 0}; + const UChar* symbol; + + u_charsToUChars("SAR", currency, UPRV_LENGTHOF(currency)); + symbol = ucurr_getName(currency, "ar_SA", UCURR_SYMBOL_NAME, &isChoiceFormat, &len, &status); + if (U_FAILURE(status)) { + log_data_err("Error: ucurr_getName returned %s (Are you missing data?)\n", u_errorName(status)); + return; + } + if (isChoiceFormat || len != 1 || symbol == NULL || u_strncmp(symbol, expectedSymbol, len) != 0) { + log_err("Error: SAR symbol for ar_SA should be U+20C1. Got length=%d first=U+%04X choice=%s\n", + len, (len > 0 && symbol != NULL) ? symbol[0] : 0, isChoiceFormat ? "true" : "false"); + } +} + void addCurrencyTest(TestNode** root); #define TESTCASE(x) addTest(root, &x, "tsformat/currtest/" #x) @@ -310,6 +330,7 @@ void addCurrencyTest(TestNode** root) TESTCASE(TestFractionDigitOverride); TESTCASE(TestPrefixSuffix); TESTCASE(TestNumericCode); + TESTCASE(TestSaudiRiyalSymbol); } #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu/icu4c/source/test/cintltst/uregiontest.c b/icu/icu4c/source/test/cintltst/uregiontest.c index 0dd896817c0..decac11892a 100644 --- a/icu/icu4c/source/test/cintltst/uregiontest.c +++ b/icu/icu4c/source/test/cintltst/uregiontest.c @@ -152,17 +152,14 @@ static KnownRegion knownRegions[] = { { "CZ" , 203, "151", URGN_TERRITORY, "150" }, { "DD" , 276, "155", URGN_TERRITORY, "150" }, { "DE" , 276, "155", URGN_TERRITORY, "150" }, - { "DG" , -1 , "QO" , URGN_TERRITORY, "009" }, { "DJ" , 262, "014", URGN_TERRITORY, "002" }, { "DK" , 208, "154", URGN_TERRITORY, "150" }, { "DM" , 212, "029", URGN_TERRITORY, "019" }, { "DO" , 214, "029", URGN_TERRITORY, "019" }, { "DZ" , 12, "015", URGN_TERRITORY, "002" }, - { "EA" , -1, "015", URGN_TERRITORY, "002" }, { "EC" , 218, "005", URGN_TERRITORY, "019" }, { "EE" , 233, "154", URGN_TERRITORY, "150" }, { "EG" , 818, "015", URGN_TERRITORY, "002" }, - { "EH" , 732, "015", URGN_TERRITORY, "002" }, { "ER" , 232, "014", URGN_TERRITORY, "002" }, { "ES" , 724, "039", URGN_TERRITORY, "150" }, { "ET" , 231, "014", URGN_TERRITORY, "002" }, @@ -199,7 +196,6 @@ static KnownRegion knownRegions[] = { { "HR" , 191, "039", URGN_TERRITORY, "150" }, { "HT" , 332, "029", URGN_TERRITORY, "019" }, { "HU" , 348, "151", URGN_TERRITORY, "150" }, - { "IC" , -1, "015", URGN_TERRITORY, "002" }, { "ID" , 360, "035", URGN_TERRITORY, "142" }, { "IE" , 372, "154", URGN_TERRITORY, "150" }, { "IL" , 376, "145", URGN_TERRITORY, "142" }, diff --git a/icu/icu4c/source/test/intltest/Makefile.in b/icu/icu4c/source/test/intltest/Makefile.in index 2e448319243..eab33ebb0b6 100644 --- a/icu/icu4c/source/test/intltest/Makefile.in +++ b/icu/icu4c/source/test/intltest/Makefile.in @@ -78,7 +78,7 @@ units_data_test.o units_router_test.o units_test.o displayoptions_test.o \ numbertest_simple.o \ cplusplus_header_api_build_test.o uchar_type_build_test.o \ ucolheaderonlytest.o usetheaderonlytest.o utfiteratortest.o utfstringtest.o \ -intltesttest.o +intltesttest.o uprefstest.o DEPS = $(OBJECTS:.o=.d) diff --git a/icu/icu4c/source/test/intltest/intltest.vcxproj b/icu/icu4c/source/test/intltest/intltest.vcxproj index ce69ec0de41..0ee503fcbe4 100644 --- a/icu/icu4c/source/test/intltest/intltest.vcxproj +++ b/icu/icu4c/source/test/intltest/intltest.vcxproj @@ -247,6 +247,7 @@ + @@ -378,6 +379,7 @@ + diff --git a/icu/icu4c/source/test/intltest/itutil.cpp b/icu/icu4c/source/test/intltest/itutil.cpp index 20c16389c0a..768507c632a 100644 --- a/icu/icu4c/source/test/intltest/itutil.cpp +++ b/icu/icu4c/source/test/intltest/itutil.cpp @@ -33,6 +33,9 @@ #include "uvectest.h" #include "aliastst.h" #include "usettest.h" +#if U_PLATFORM_USES_ONLY_WIN32_API && UCONFIG_USE_WINDOWS_PREFERENCES_LIBRARY + #include "uprefstest.h" +#endif extern IntlTest *createBytesTrieTest(); #if !UCONFIG_NO_COLLATION @@ -76,6 +79,9 @@ void IntlTestUtilities::runIndexedTest( int32_t index, UBool exec, const char* & TESTCASE_AUTO_CLASS(LocaleAliasTest); TESTCASE_AUTO_CLASS(UnicodeSetTest); TESTCASE_AUTO_CLASS(ErrorCodeTest); +#if U_PLATFORM_USES_ONLY_WIN32_API && UCONFIG_USE_WINDOWS_PREFERENCES_LIBRARY + TESTCASE_AUTO_CLASS(UPrefsTest); +#endif TESTCASE_AUTO_CREATE_CLASS(LocalPointerTest); TESTCASE_AUTO_CREATE_CLASS(BytesTrieTest); TESTCASE_AUTO_CREATE_CLASS(UCharsTrieTest); diff --git a/icu/icu4c/source/test/intltest/regiontst.cpp b/icu/icu4c/source/test/intltest/regiontst.cpp index cdbeac793a7..f2b06fd3bb8 100644 --- a/icu/icu4c/source/test/intltest/regiontst.cpp +++ b/icu/icu4c/source/test/intltest/regiontst.cpp @@ -124,17 +124,14 @@ static KnownRegion knownRegions[] = { { "CZ" , 203, "151", URGN_TERRITORY, "150" }, { "DD" , 276, "155", URGN_TERRITORY, "150" }, { "DE" , 276, "155", URGN_TERRITORY, "150" }, - { "DG" , -1 , "QO" , URGN_TERRITORY, "009" }, { "DJ" , 262, "014", URGN_TERRITORY, "002" }, { "DK" , 208, "154", URGN_TERRITORY, "150" }, { "DM" , 212, "029", URGN_TERRITORY, "019" }, { "DO" , 214, "029", URGN_TERRITORY, "019" }, { "DZ" , 12, "015", URGN_TERRITORY, "002" }, - { "EA" , -1, "015", URGN_TERRITORY, "002" }, { "EC" , 218, "005", URGN_TERRITORY, "019" }, { "EE" , 233, "154", URGN_TERRITORY, "150" }, { "EG" , 818, "015", URGN_TERRITORY, "002" }, - { "EH" , 732, "015", URGN_TERRITORY, "002" }, { "ER" , 232, "014", URGN_TERRITORY, "002" }, { "ES" , 724, "039", URGN_TERRITORY, "150" }, { "ET" , 231, "014", URGN_TERRITORY, "002" }, @@ -171,7 +168,6 @@ static KnownRegion knownRegions[] = { { "HR" , 191, "039", URGN_TERRITORY, "150" }, { "HT" , 332, "029", URGN_TERRITORY, "019" }, { "HU" , 348, "151", URGN_TERRITORY, "150" }, - { "IC" , -1, "015", URGN_TERRITORY, "002" }, { "ID" , 360, "035", URGN_TERRITORY, "142" }, { "IE" , 372, "154", URGN_TERRITORY, "150" }, { "IL" , 376, "145", URGN_TERRITORY, "142" }, diff --git a/icu/icu4c/source/test/intltest/uprefstest.cpp b/icu/icu4c/source/test/intltest/uprefstest.cpp new file mode 100644 index 00000000000..69c5265eed1 --- /dev/null +++ b/icu/icu4c/source/test/intltest/uprefstest.cpp @@ -0,0 +1,438 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +#include "uprefstest.h" +#if U_PLATFORM_USES_ONLY_WIN32_API && UCONFIG_USE_WINDOWS_PREFERENCES_LIBRARY + +#define ARRAY_SIZE 512 + +std::wstring UPrefsTest::language = L""; +std::wstring UPrefsTest::currency = L""; +std::wstring UPrefsTest::hourCycle = L""; +int32_t UPrefsTest::firstday = 0; +int32_t UPrefsTest::measureSystem = 0; +CALID UPrefsTest::calendar = 0; + +void UPrefsTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) +{ + if (exec) logln("TestSuite UPrefsTest: "); + TESTCASE_AUTO_BEGIN; + TESTCASE_AUTO(TestGetDefaultLocaleAsBCP47Tag); + TESTCASE_AUTO(TestBCP47TagWithSorting); + TESTCASE_AUTO(TestBCP47TagChineseSimplified); + TESTCASE_AUTO(TestBCP47TagChineseSortingStroke); + TESTCASE_AUTO(TestBCP47TagJapanCalendar); + TESTCASE_AUTO(TestUseNeededBuffer); + TESTCASE_AUTO(TestGetNeededBuffer); + TESTCASE_AUTO(TestGetUnsupportedSorting); + TESTCASE_AUTO(Get24HourCycleMixed); + TESTCASE_AUTO(Get12HourCycleMixed); + TESTCASE_AUTO(Get12HourCycleMixed2); + TESTCASE_AUTO(Get12HourCycle); + TESTCASE_AUTO(Get12HourCycle2); + TESTCASE_AUTO_END; +} + +int32_t UPrefsTest::MockGetLocaleInfoEx(LPCWSTR lpLocaleName, LCTYPE LCType, LPWSTR lpLCData, + int cchData, UErrorCode *status) +{ + switch (LCType) + { + case LOCALE_SNAME: + if (cchData == 0) + { + *status = U_ZERO_ERROR; + return language.length() + 1; + } + + if (language.length() + 1 > cchData) + { + *status = U_BUFFER_OVERFLOW_ERROR; + return 0; + } + wcsncpy(lpLCData, language.c_str(), cchData); + *status = U_ZERO_ERROR; + return language.length(); + + case LOCALE_ICALENDARTYPE | LOCALE_RETURN_NUMBER: + if (cchData == 0) + { + *status = U_ZERO_ERROR; + return 2; + } + if (cchData < 2) + { + *status = U_BUFFER_OVERFLOW_ERROR; + return 0; + } + *(reinterpret_cast(lpLCData)) = calendar; + *status = U_ZERO_ERROR; + return 2; + + case LOCALE_SINTLSYMBOL: + if (cchData == 0) + { + *status = U_ZERO_ERROR; + return currency.length() + 1; + } + if (currency.length() + 1 > cchData) + { + *status = U_BUFFER_OVERFLOW_ERROR; + return 0; + } + wcsncpy(lpLCData, currency.c_str(), cchData); + *status = U_ZERO_ERROR; + return currency.length(); + + case LOCALE_IFIRSTDAYOFWEEK | LOCALE_RETURN_NUMBER: + if (cchData == 0) + { + *status = U_ZERO_ERROR; + return 2; + } + if (cchData < 2) + { + *status = U_BUFFER_OVERFLOW_ERROR; + return 0; + } + + *(reinterpret_cast(lpLCData)) = firstday; + *status = U_ZERO_ERROR; + return 2; + + case LOCALE_STIMEFORMAT: + if (cchData == 0) + { + *status = U_ZERO_ERROR; + return hourCycle.length() + 1; + } + + if (hourCycle.length() + 1 > cchData) + { + *status = U_BUFFER_OVERFLOW_ERROR; + return 0; + } + wcsncpy(lpLCData, hourCycle.c_str(), cchData); + *status = U_ZERO_ERROR; + return 0; + + case LOCALE_IMEASURE | LOCALE_RETURN_NUMBER: + if (cchData == 0) + { + *status = U_ZERO_ERROR; + return 2; + } + + if (cchData < 2) + { + *status = U_BUFFER_OVERFLOW_ERROR; + return 0; + } + *(reinterpret_cast(lpLCData)) = measureSystem; + *status = U_ZERO_ERROR; + return 2; + + default: + *status = U_INTERNAL_PROGRAM_ERROR; + return 0; + } +} + +// The code above is independent of the library itself, but for the code below this point, +// we need to include the library to be able to use the definitions of the API uprefs_getBCP47Tag +#include "uprefs.cpp" + +void UPrefsTest::TestGetDefaultLocaleAsBCP47Tag() +{ + char languageBuffer[ARRAY_SIZE] = {0}; + language = L"en-US"; + currency = L"USD"; + hourCycle = L"HH:mm:ss"; + firstday = 0; + measureSystem = 1; + calendar = CAL_GREGORIAN; + UErrorCode status = U_ZERO_ERROR; + const char* expectedValue = "en-US-u-ca-gregory-cu-usd-fw-mon-hc-h23-ms-ussystem"; + + if ( uprefs_getBCP47Tag(languageBuffer, ARRAY_SIZE, &status) != 52) + { + errln("Expected length to be 52, but got: %d\n",uprv_strlen(languageBuffer)); + } + if ( uprv_strcmp(expectedValue, languageBuffer) != 0) + { + errln("Expected BCP47Tag to be %s, but got: %s\n", expectedValue, languageBuffer); + } +} + +void UPrefsTest::TestBCP47TagWithSorting() +{ + char languageBuffer[ARRAY_SIZE] = {0}; + language = L"de-DE_phoneb"; + currency = L"EUR"; + hourCycle = L"HH:mm:ss"; + firstday = 0; + measureSystem = 1; + calendar = CAL_GREGORIAN; + UErrorCode status = U_ZERO_ERROR; + char* expectedValue = "de-DE-u-ca-gregory-co-phonebk-cu-eur-fw-mon-hc-h23-ms-ussystem"; + + if ( uprefs_getBCP47Tag(languageBuffer, ARRAY_SIZE, &status) != 63) + { + errln("Expected length to be 63, but got: %d\n",uprv_strlen(languageBuffer)); + } + if ( uprv_strcmp(expectedValue, languageBuffer) != 0) + { + errln("Expected BCP47Tag to be %s, but got: %s\n", expectedValue, languageBuffer); + } +} + +void UPrefsTest::TestBCP47TagChineseSimplified() +{ + char languageBuffer[ARRAY_SIZE] = {0}; + language = L"zh-Hans-HK"; + currency = L"EUR"; + hourCycle = L"hh:mm:ss"; + firstday = 2; + measureSystem = 1; + calendar = CAL_GREGORIAN; + UErrorCode status = U_ZERO_ERROR; + char* expectedValue = "zh-Hans-HK-u-ca-gregory-cu-eur-fw-wed-hc-h12-ms-ussystem"; + + if ( uprefs_getBCP47Tag(languageBuffer, ARRAY_SIZE, &status) != 57) + { + errln("Expected length to be 57, but got: %d\n",uprv_strlen(languageBuffer)); + } + if ( uprv_strcmp(expectedValue, languageBuffer) != 0) + { + errln("Expected BCP47Tag to be %s, but got: %s\n", expectedValue, languageBuffer); + } +} + +void UPrefsTest::TestBCP47TagChineseSortingStroke() +{ + char languageBuffer[ARRAY_SIZE] = {0}; + language = L"zh-SG_stroke"; + currency = L"EUR"; + hourCycle = L"hh:mm:ss"; + firstday = 2; + measureSystem = 0; + calendar = CAL_GREGORIAN; + UErrorCode status = U_ZERO_ERROR; + char* expectedValue = "zh-SG-u-ca-gregory-co-stroke-cu-eur-fw-wed-hc-h12-ms-metric"; + + if ( uprefs_getBCP47Tag(languageBuffer, ARRAY_SIZE, &status) != 60) + { + errln("Expected length to be 60, but got: %d\n",uprv_strlen(languageBuffer)); + } + if ( uprv_strcmp(expectedValue, languageBuffer) != 0) + { + errln("Expected BCP47Tag to be %s, but got: %s\n", expectedValue, languageBuffer); + } +} + +void UPrefsTest::TestBCP47TagJapanCalendar() +{ + char languageBuffer[ARRAY_SIZE] = {0}; + language = L"ja-JP"; + currency = L"MXN"; + hourCycle = L"hh:mm:ss"; + firstday = 1; + measureSystem = 0; + calendar = CAL_JAPAN; + UErrorCode status = U_ZERO_ERROR; + char* expectedValue = "ja-JP-u-ca-japanese-cu-mxn-fw-tue-hc-h12-ms-metric"; + + if ( uprefs_getBCP47Tag(languageBuffer, ARRAY_SIZE, &status) != 51) + { + errln("Expected length to be 51, but got: %d\n",uprv_strlen(languageBuffer)); + } + if ( uprv_strcmp(expectedValue, languageBuffer) != 0) + { + errln("Expected BCP47Tag to be %s, but got: %s\n", expectedValue, languageBuffer); + } +} + +void UPrefsTest::TestUseNeededBuffer() +{ + char languageBuffer[ARRAY_SIZE] = {0}; + language = L"ja-JP"; + currency = L"MXN"; + hourCycle = L"hh:mm:ss"; + firstday = 1; + measureSystem = 0; + calendar = CAL_THAI; + UErrorCode status = U_ZERO_ERROR; + char* expectedValue = "ja-JP-u-ca-buddhist-cu-mxn-fw-tue-hc-h12-ms-metric"; + + int32_t neededBufferSize = uprefs_getBCP47Tag(nullptr, 0, &status); + + if ( uprefs_getBCP47Tag(languageBuffer, neededBufferSize, &status) != 51) + { + errln("Expected length to be 51, but got: %d\n",uprv_strlen(languageBuffer)); + } + if ( uprv_strcmp(expectedValue, languageBuffer) != 0) + { + errln("Expected BCP47Tag to be %s, but got: %s\n", expectedValue, languageBuffer); + } +} + +void UPrefsTest::TestGetNeededBuffer() +{ + char languageBuffer[ARRAY_SIZE] = {0}; + language = L"zh-SG_stroke"; + currency = L"MXN"; + hourCycle = L"hh:mm:ss"; + firstday = 1; + measureSystem = 0; + calendar = CAL_THAI; + UErrorCode status = U_ZERO_ERROR; + char* expectedValue = "zh-SG-u-ca-buddhist-co-stroke-cu-mxn-fw-tue-hc-h12-ms-metric"; + + int32_t neededBufferSize = uprefs_getBCP47Tag(nullptr, 0, &status); + + if ( neededBufferSize != 61) + { + errln("Expected buffer size to be 61, but got: %d\n",uprv_strlen(languageBuffer)); + } + if ( uprefs_getBCP47Tag(languageBuffer, neededBufferSize, &status) != 61) + { + errln("Expected length to be 61, but got: %d\n",uprv_strlen(languageBuffer)); + } + if ( uprv_strcmp(expectedValue, languageBuffer) != 0) + { + errln("Expected BCP47Tag to be %s, but got: %s\n", expectedValue, languageBuffer); + } +} + +void UPrefsTest::TestGetUnsupportedSorting() +{ + char languageBuffer[ARRAY_SIZE] = {0}; + language = L"hu-HU_technl"; + currency = L"MXN"; + hourCycle = L"hh:mm:ss"; + firstday = 1; + measureSystem = 0; + calendar = CAL_THAI; + UErrorCode status = U_ZERO_ERROR; + char* expectedValue = "hu-HU-u-ca-buddhist-cu-mxn-fw-tue-hc-h12-ms-metric"; + + if ( uprefs_getBCP47Tag(languageBuffer, ARRAY_SIZE, &status) != 51) + { + errln("Expected length to be 51, but got: %d\n",uprv_strlen(languageBuffer)); + } + if ( uprv_strcmp(expectedValue, languageBuffer) != 0) + { + errln("Expected BCP47Tag to be %s, but got: %s\n", expectedValue, languageBuffer); + } +} + +void UPrefsTest::Get24HourCycleMixed() +{ + char languageBuffer[ARRAY_SIZE] = {0}; + language = L"ja-JP"; + currency = L"MXN"; + hourCycle = L"HHhh:mm:ss"; + firstday = 1; + measureSystem = 0; + calendar = CAL_THAI; + UErrorCode status = U_ZERO_ERROR; + char* expectedValue = "ja-JP-u-ca-buddhist-cu-mxn-fw-tue-hc-h23-ms-metric"; + + if (uprefs_getBCP47Tag(languageBuffer, ARRAY_SIZE, &status) != 51) + { + errln("Expected length to be 51, but got: %d\n", uprv_strlen(languageBuffer)); + } + if (uprv_strcmp(expectedValue, languageBuffer) != 0) + { + errln("Expected BCP47Tag to be %s, but got: %s\n", expectedValue, languageBuffer); + } +} + +void UPrefsTest::Get12HourCycleMixed() +{ + char languageBuffer[ARRAY_SIZE] = {0}; + language = L"ja-JP"; + currency = L"MXN"; + hourCycle = L"hHhH:mm:ss"; + firstday = 1; + measureSystem = 0; + calendar = CAL_THAI; + UErrorCode status = U_ZERO_ERROR; + char* expectedValue = "ja-JP-u-ca-buddhist-cu-mxn-fw-tue-hc-h12-ms-metric"; + + if (uprefs_getBCP47Tag(languageBuffer, ARRAY_SIZE, &status) != 51) + { + errln("Expected length to be 51, but got: %d\n", uprv_strlen(languageBuffer)); + } + if (uprv_strcmp(expectedValue, languageBuffer) != 0) + { + errln("Expected BCP47Tag to be %s, but got: %s\n", expectedValue, languageBuffer); + } +} + + +void UPrefsTest::Get12HourCycleMixed2() +{ + char languageBuffer[ARRAY_SIZE] = {0}; + language = L"ja-JP"; + currency = L"MXN"; + hourCycle = L"hH''h'H'H:mm:ss"; + firstday = 1; + measureSystem = 0; + calendar = CAL_THAI; + UErrorCode status = U_ZERO_ERROR; + char* expectedValue = "ja-JP-u-ca-buddhist-cu-mxn-fw-tue-hc-h12-ms-metric"; + + if (uprefs_getBCP47Tag(languageBuffer, ARRAY_SIZE, &status) != 51) + { + errln("Expected length to be 51, but got: %d\n", uprv_strlen(languageBuffer)); + } + if (uprv_strcmp(expectedValue, languageBuffer) != 0) + { + errln("Expected BCP47Tag to be %s, but got: %s\n", expectedValue, languageBuffer); + } +} + +void UPrefsTest::Get12HourCycle() +{ + char languageBuffer[ARRAY_SIZE] = {0}; + language = L"ja-JP"; + currency = L"MXN"; + hourCycle = L"h'H'h:mm:ss"; + firstday = 1; + measureSystem = 0; + calendar = CAL_THAI; + UErrorCode status = U_ZERO_ERROR; + char* expectedValue = "ja-JP-u-ca-buddhist-cu-mxn-fw-tue-hc-h12-ms-metric"; + + if (uprefs_getBCP47Tag(languageBuffer, ARRAY_SIZE, &status) != 51) + { + errln("Expected length to be 51, but got: %d\n", uprv_strlen(languageBuffer)); + } + if (uprv_strcmp(expectedValue, languageBuffer) != 0) + { + errln("Expected BCP47Tag to be %s, but got: %s\n", expectedValue, languageBuffer); + } +} + +void UPrefsTest::Get12HourCycle2() +{ + char languageBuffer[ARRAY_SIZE] = {0}; + language = L"ja-JP"; + currency = L"MXN"; + hourCycle = L"'H'h'H'h:mm:ss"; + firstday = 1; + measureSystem = 0; + calendar = CAL_THAI; + UErrorCode status = U_ZERO_ERROR; + char* expectedValue = "ja-JP-u-ca-buddhist-cu-mxn-fw-tue-hc-h12-ms-metric"; + + if (uprefs_getBCP47Tag(languageBuffer, ARRAY_SIZE, &status) != 51) + { + errln("Expected length to be 51, but got: %d\n", uprv_strlen(languageBuffer)); + } + if (uprv_strcmp(expectedValue, languageBuffer) != 0) + { + errln("Expected BCP47Tag to be %s, but got: %s\n", expectedValue, languageBuffer); + } +} +#endif //U_PLATFORM_USES_ONLY_WIN32_API && UCONFIG_USE_WINDOWS_PREFERENCES_LIBRARY \ No newline at end of file diff --git a/icu/icu4c/source/test/intltest/uprefstest.h b/icu/icu4c/source/test/intltest/uprefstest.h new file mode 100644 index 00000000000..2f3e6515b06 --- /dev/null +++ b/icu/icu4c/source/test/intltest/uprefstest.h @@ -0,0 +1,50 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html +#ifndef UPREFSTEST_H +#define UPREFSTEST_H + +#include "unicode/platform.h" +#if U_PLATFORM_USES_ONLY_WIN32_API && UCONFIG_USE_WINDOWS_PREFERENCES_LIBRARY +// We define UPREFS_TEST to use the mock version of GetLocaleInfoEx(), which +// allows us to simulate its behaviour and determine if the results given by the +// API align with what we expect to receive +#define UPREFS_TEST 1 + + +#include "windows.h" +#include "intltest.h" +#include "uprefs.h" + +class UPrefsTest: public IntlTest { +private: + static std::wstring language; + static std::wstring currency; + static std::wstring hourCycle; + static int32_t firstday; + static int32_t measureSystem; + static CALID calendar; + +public: + UPrefsTest(){}; + virtual ~UPrefsTest(){}; + + virtual void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = NULL) override; + int32_t MockGetLocaleInfoEx(LPCWSTR lpLocaleName, LCTYPE LCType, LPWSTR lpLCData, int cchData, UErrorCode* status); + void TestGetDefaultLocaleAsBCP47Tag(); + void TestBCP47TagWithSorting(); + void TestBCP47TagChineseSimplified(); + void TestBCP47TagChineseSortingStroke(); + void TestBCP47TagJapanCalendar(); + void TestUseNeededBuffer(); + void TestGetNeededBuffer(); + void TestGetUnsupportedSorting(); + void Get24HourCycleMixed(); + void Get12HourCycleMixed(); + void Get12HourCycleMixed2(); + void Get12HourCycle(); + void Get12HourCycle2(); +}; + + +#endif //U_PLATFORM_USES_ONLY_WIN32_API && UCONFIG_USE_WINDOWS_PREFERENCES_LIBRARY +#endif //UPREFSTEST_H \ No newline at end of file diff --git a/icu/icu4c/source/tools/toolutil/package.h b/icu/icu4c/source/tools/toolutil/package.h index ea60c13a74a..3a1d3cd23d9 100644 --- a/icu/icu4c/source/tools/toolutil/package.h +++ b/icu/icu4c/source/tools/toolutil/package.h @@ -27,7 +27,7 @@ // .dat package file representation ---------------------------------------- *** -#define STRING_STORE_SIZE 100000 +#define STRING_STORE_SIZE 200000 #define MAX_PKG_NAME_LENGTH 64 typedef void CheckDependency(void *context, const char *itemName, const char *targetName); diff --git a/icu/icu4c/source/tools/toolutil/pkg_gencmn.cpp b/icu/icu4c/source/tools/toolutil/pkg_gencmn.cpp index c1a46e9aed1..054208170af 100644 --- a/icu/icu4c/source/tools/toolutil/pkg_gencmn.cpp +++ b/icu/icu4c/source/tools/toolutil/pkg_gencmn.cpp @@ -22,7 +22,13 @@ #define STRING_STORE_SIZE 200000 -#define COMMON_DATA_NAME U_ICUDATA_NAME +// MSFT-Change: In the Windows OS ICU build, we only have one data package, and we use a versionless name in filename. +#if defined(ICU_DATA_DIR_WINDOWS) +# define COMMON_DATA_NAME "icudtl" +#else +# define COMMON_DATA_NAME U_ICUDATA_NAME +#endif + #define DATA_TYPE "dat" /* ICU package data file format (.dat files) ------------------------------- *** diff --git a/icu/tools/cldr/cldr-to-icu/src/main/java/org/unicode/icu/tool/cldrtoicu/Cldr2IcuCliOptions.java b/icu/tools/cldr/cldr-to-icu/src/main/java/org/unicode/icu/tool/cldrtoicu/Cldr2IcuCliOptions.java index d9b46014438..9ff85b38d86 100644 --- a/icu/tools/cldr/cldr-to-icu/src/main/java/org/unicode/icu/tool/cldrtoicu/Cldr2IcuCliOptions.java +++ b/icu/tools/cldr/cldr-to-icu/src/main/java/org/unicode/icu/tool/cldrtoicu/Cldr2IcuCliOptions.java @@ -380,7 +380,9 @@ private void validateEnvironment() { if (!new File(icuDir).isDirectory() || ! new File(icuDir, "icu4c").isDirectory() - || ! new File(icuDir, "icu4j").isDirectory() + // MSFT-Change: microsoft/icu fork is icu4c-only; no icu4j source tree. + // The Maven dependency on icu4j (used by TransformsMapper) is still + // resolved from ~/.m2 at runtime, so source-tree absence is harmless. || ! new File(icuDir, "tools/cldr/cldr-to-icu").isDirectory() || ! new File(icuDir, "tools/cldr/cldr-to-icu/pom.xml").isFile()) { System.err.println("The `" + icuDir + "` directory does not look like a valid icu root.");