From 5a77fd9d110fba9f6fd4b16a5b969d64facd766e Mon Sep 17 00:00:00 2001 From: gnrunge Date: Wed, 8 Jun 2022 14:26:47 -0700 Subject: [PATCH] ICU-21997 Fixes currency code retrieval for locale: instead of selecting the first currency in the list now select the first legal tender currency in the list. Or the first currency if the list has no legal tender currencies (which is the previous behaviour). ICU-21997 Removed an overlooked earlier unit test attempt. ICU-21997 Shields C++ unit test from compilation when configuration flag UCONFIG_NO_FORMATTING is set. --- icu4c/source/common/ucurr.cpp | 29 +++++++++++++++++-- icu4c/source/test/intltest/loctest.cpp | 19 ++++++++++++ icu4c/source/test/intltest/loctest.h | 1 + .../core/src/com/ibm/icu/util/Currency.java | 7 ++++- .../ibm/icu/dev/test/util/CurrencyTest.java | 11 +++++++ 5 files changed, 63 insertions(+), 4 deletions(-) diff --git a/icu4c/source/common/ucurr.cpp b/icu4c/source/common/ucurr.cpp index 2d7cb7ad0c8..8e71f4afb36 100644 --- a/icu4c/source/common/ucurr.cpp +++ b/icu4c/source/common/ucurr.cpp @@ -566,9 +566,32 @@ ucurr_forLocale(const char* locale, UResourceBundle *rb = ures_openDirect(U_ICUDATA_CURR, CURRENCY_DATA, &localStatus); UResourceBundle *cm = ures_getByKey(rb, CURRENCY_MAP, rb, &localStatus); UResourceBundle *countryArray = ures_getByKey(rb, id, cm, &localStatus); - UResourceBundle *currencyReq = ures_getByIndex(countryArray, 0, NULL, &localStatus); - s = ures_getStringByKey(currencyReq, "id", &resLen, &localStatus); - ures_close(currencyReq); + // https://unicode-org.atlassian.net/browse/ICU-21997 + // Prefer to use currencies that are legal tender. + if (U_SUCCESS(localStatus)) { + int32_t arrayLength = ures_getSize(countryArray); + for (int32_t i = 0; i < arrayLength; ++i) { + LocalUResourceBundlePointer currencyReq( + ures_getByIndex(countryArray, i, nullptr, &localStatus)); + // The currency is legal tender if it is *not* marked with tender{"false"}. + UErrorCode tenderStatus = localStatus; + const UChar *tender = + ures_getStringByKey(currencyReq.getAlias(), "tender", nullptr, &tenderStatus); + bool isTender = U_FAILURE(tenderStatus) || u_strcmp(tender, u"false") != 0; + if (!isTender && s != nullptr) { + // We already have a non-tender currency. Ignore all following non-tender ones. + continue; + } + // Fetch the currency code. + s = ures_getStringByKey(currencyReq.getAlias(), "id", &resLen, &localStatus); + if (isTender) { + break; + } + } + if (U_SUCCESS(localStatus) && s == nullptr) { + localStatus = U_MISSING_RESOURCE_ERROR; + } + } ures_close(countryArray); } diff --git a/icu4c/source/test/intltest/loctest.cpp b/icu4c/source/test/intltest/loctest.cpp index a68dc117af1..ebb9dcd2826 100644 --- a/icu4c/source/test/intltest/loctest.cpp +++ b/icu4c/source/test/intltest/loctest.cpp @@ -288,6 +288,9 @@ void LocaleTest::runIndexedTest( int32_t index, UBool exec, const char* &name, c TESTCASE_AUTO(TestNullDereferenceWrite21597); TESTCASE_AUTO(TestLongLocaleSetKeywordAssign); TESTCASE_AUTO(TestLongLocaleSetKeywordMoveAssign); +#if !UCONFIG_NO_FORMATTING + TESTCASE_AUTO(TestSierraLeoneCurrency21997); +#endif TESTCASE_AUTO_END; } @@ -6614,3 +6617,19 @@ void LocaleTest::TestNullDereferenceWrite21597() { l.canonicalize(status); status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR); } +#if !UCONFIG_NO_FORMATTING +void LocaleTest::TestSierraLeoneCurrency21997() { + // Check that currency of Sierra Leone is SLL (which is legal tender) + // and not the newer currency SLE (which is not legal tender), as of CLDR 41. + // Test will fail once SLE is declared legal. + UnicodeString sllStr("SLL", ""), resultStr; + UChar tmp[4]; + UErrorCode status = U_ZERO_ERROR; + + ucurr_forLocale("en_SL", tmp, 4, &status); + resultStr.setTo(tmp); + if (sllStr != resultStr) { + errcheckln(status, "Fail: en_SL didn't return SLL - %s", u_errorName(status)); + } +} +#endif diff --git a/icu4c/source/test/intltest/loctest.h b/icu4c/source/test/intltest/loctest.h index 316f14e89f1..2a8ed56760d 100644 --- a/icu4c/source/test/intltest/loctest.h +++ b/icu4c/source/test/intltest/loctest.h @@ -160,6 +160,7 @@ public: void TestNullDereferenceWrite21597(); void TestLongLocaleSetKeywordAssign(); void TestLongLocaleSetKeywordMoveAssign(); + void TestSierraLeoneCurrency21997(); private: void _checklocs(const char* label, diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/Currency.java b/icu4j/main/classes/core/src/com/ibm/icu/util/Currency.java index 03bcc1f8f54..d94d1f65915 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/util/Currency.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/util/Currency.java @@ -278,7 +278,12 @@ public class Currency extends MeasureUnit { private static Currency loadCurrency(String key) { String region = key; CurrencyMetaInfo info = CurrencyMetaInfo.getInstance(); - List list = info.currencies(CurrencyFilter.onRegion(region)); + // https://unicode-org.atlassian.net/browse/ICU-21997 + // Prefer to use currencies that are legal tender. + List list = info.currencies(CurrencyFilter.onRegion(region).withTender()); + if (list.isEmpty()) { + list = info.currencies(CurrencyFilter.onRegion(region)); + } if (!list.isEmpty()) { String code = list.get(0); return getInstance(code); diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/CurrencyTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/CurrencyTest.java index 50e8e1438dc..e3bc6858e59 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/CurrencyTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/CurrencyTest.java @@ -983,4 +983,15 @@ public class CurrencyTest extends TestFmwk { public void TestCurrencyDataCtor() throws Exception { checkDefaultPrivateConstructor(CurrencyData.class); } + @Test + public void testSierraLeoneCurrency21997() { + // Check that currency of Sierra Leone is SLL (which is legal tender) + // and not the newer currency SLE (which is not legal tender), as of CLDR 41. + // Test will fail once SLE is declared legal. + Currency currency = Currency.getInstance(ULocale.forLanguageTag("en-SL")); + String result = currency.getCurrencyCode(); + if (!"SLL".equals(result)) { + errln("Currency code of en-SL is not SLL but " + result); + } + } }