From 5174ad5511d11a6f21daf8588cc2420bf03c922c Mon Sep 17 00:00:00 2001 From: Azamshul Azizy Date: Thu, 30 May 2019 18:53:13 +0900 Subject: [PATCH] ICU-20631 Fix desired currency not set for some NumberFormat currency styles --- icu4c/source/test/intltest/numfmtst.cpp | 45 +++++++++++++++++++ icu4c/source/test/intltest/numfmtst.h | 1 + .../ibm/icu/text/NumberFormatServiceShim.java | 7 ++- .../icu/dev/test/format/NumberFormatTest.java | 39 ++++++++++++++++ 4 files changed, 90 insertions(+), 2 deletions(-) diff --git a/icu4c/source/test/intltest/numfmtst.cpp b/icu4c/source/test/intltest/numfmtst.cpp index 5c0e8490767..8e7befe2dea 100644 --- a/icu4c/source/test/intltest/numfmtst.cpp +++ b/icu4c/source/test/intltest/numfmtst.cpp @@ -169,6 +169,7 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n TESTCASE_AUTO(TestRoundingScientific10542); TESTCASE_AUTO(TestZeroScientific10547); TESTCASE_AUTO(TestAccountingCurrency); + TESTCASE_AUTO(TestCurrencyFormatForMissingLocale); TESTCASE_AUTO(TestEquality); TESTCASE_AUTO(TestCurrencyUsage); TESTCASE_AUTO(TestDoubleLimit11439); @@ -8014,6 +8015,50 @@ void NumberFormatTest::TestAccountingCurrency() { (Formattable)(double)-1000.5, UnicodeString("(\\uFFE51,000)").unescape(), FALSE, status); expect(NumberFormat::createInstance("de_DE", style, status), (Formattable)(double)-23456.7, UnicodeString("-23.456,70\\u00A0\\u20AC").unescape(), TRUE, status); + expect(NumberFormat::createInstance("en_ID", style, status), + (Formattable)(double)0, UnicodeString("IDR\\u00A00.00").unescape(), TRUE, status); + expect(NumberFormat::createInstance("en_ID", style, status), + (Formattable)(double)-0.2, UnicodeString("(IDR\\u00A00.20)").unescape(), TRUE, status); + expect(NumberFormat::createInstance("sh_ME", style, status), + (Formattable)(double)0, UnicodeString("0,00\\u00A0\\u20AC").unescape(), TRUE, status); + expect(NumberFormat::createInstance("sh_ME", style, status), + (Formattable)(double)-0.2, UnicodeString("(0,20\\u00A0\\u20AC)").unescape(), TRUE, status); +} + +/** + * ICU4J has the behavior explained below, but ICU4C is not affected. Test is added to prevent regression. + * + * en_ID/sh_ME uses language only locales en/sh which requires NumberFormatServiceShim to fill in the currency, but + * prior to ICU-20631, currency was not filled in for accounting, cash and standard, so currency placeholder was + * used instead of the desired locale's currency. + */ +void NumberFormatTest::TestCurrencyFormatForMissingLocale() { + IcuTestErrorCode status(*this, "TestCurrencyFormatForMissingLocale"); + Locale locale = Locale::createCanonical("sh_ME"); + + LocalPointer curFmt(NumberFormat::createInstance(locale, UNUM_CURRENCY, status)); + assertEquals("Currency instance is not for the desired locale for CURRENCYSTYLE", curFmt->getCurrency(), "EUR"); + UnicodeString currBuf; + curFmt->format(-1234.5, currBuf); + assertEquals("NumberFormat format outputs wrong value for CURRENCYSTYLE", u"-1.234,50\u00A0\u20AC", currBuf); + + LocalPointer accFmt(NumberFormat::createInstance(locale, UNUM_CURRENCY_ACCOUNTING, status)); + assertEquals("Currency instance is not for the desired locale for ACCOUNTINGCURRENCYSTYLE", accFmt->getCurrency(), "EUR"); + UnicodeString accBuf; + accFmt->format(-1234.5, accBuf); + assertEquals("NumberFormat format outputs wrong value for ACCOUNTINGCURRENCYSTYLE", u"(1.234,50\u00A0\u20AC)", accBuf); + + LocalPointer cashFmt(NumberFormat::createInstance(locale, UNUM_CASH_CURRENCY, status)); + assertEquals("Currency instance is not for the desired locale for CASHCURRENCYSTYLE", cashFmt->getCurrency(), "EUR"); + UnicodeString cashBuf; + cashFmt->format(-1234.5, cashBuf); + assertEquals("NumberFormat format outputs wrong value for CASHCURRENCYSTYLE", u"-1.234,50\u00A0\u20AC", cashBuf); + + LocalPointer stdFmt(NumberFormat::createInstance(locale, UNUM_CURRENCY_STANDARD, status)); + assertEquals("Currency instance is not for the desired locale for STANDARDCURRENCYSTYLE", stdFmt->getCurrency(), "EUR"); + UnicodeString stdBuf; + stdFmt->format(-1234.5, stdBuf); + assertEquals("NumberFormat format outputs wrong value for STANDARDCURRENCYSTYLE", u"-1.234,50\u00A0\u20AC", stdBuf); } // for #5186 diff --git a/icu4c/source/test/intltest/numfmtst.h b/icu4c/source/test/intltest/numfmtst.h index 7dbd31c2df6..66c4b2d9c73 100644 --- a/icu4c/source/test/intltest/numfmtst.h +++ b/icu4c/source/test/intltest/numfmtst.h @@ -231,6 +231,7 @@ class NumberFormatTest: public CalendarTimeZoneTest { void TestRoundingScientific10542(); void TestZeroScientific10547(); void TestAccountingCurrency(); + void TestCurrencyFormatForMissingLocale(); void TestEquality(); void TestCurrencyUsage(); diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormatServiceShim.java b/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormatServiceShim.java index 7bba2d61257..9a657ceed7d 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormatServiceShim.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/NumberFormatServiceShim.java @@ -93,8 +93,11 @@ class NumberFormatServiceShim extends NumberFormat.NumberFormatShim { // If we are creating a currency type formatter, then we may have to set the currency // explicitly, since the actualLoc may be different than the desiredLocale if ( choice == NumberFormat.CURRENCYSTYLE || - choice == NumberFormat.ISOCURRENCYSTYLE || - choice == NumberFormat.PLURALCURRENCYSTYLE) { + choice == NumberFormat.ISOCURRENCYSTYLE || + choice == NumberFormat.PLURALCURRENCYSTYLE || + choice == NumberFormat.ACCOUNTINGCURRENCYSTYLE || + choice == NumberFormat.CASHCURRENCYSTYLE || + choice == NumberFormat.STANDARDCURRENCYSTYLE) { fmt.setCurrency(Currency.getInstance(desiredLocale)); } diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java index f6b57523149..dcbf19d8129 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java @@ -4416,6 +4416,18 @@ public class NumberFormatTest extends TestFmwk { {"ja_JP", "-1000.5", "-¥1,000", "-¥1,000", "(¥1,000)", "false"}, {"ja_JP@cf=account", "-1000.5", "(¥1,000)", "-¥1,000", "(¥1,000)", "false"}, {"de_DE", "-23456.7", "-23.456,70\u00A0€", "-23.456,70\u00A0€", "-23.456,70\u00A0€", "true" }, + {"en_ID", "1234.5", "IDR 1,234.50", "IDR 1,234.50", "IDR 1,234.50", "true"}, + {"en_ID@cf=account", "1234.5", "IDR 1,234.50", "IDR 1,234.50", "IDR 1,234.50", "true"}, + {"en_ID@cf=standard", "1234.5", "IDR 1,234.50", "IDR 1,234.50", "IDR 1,234.50", "true"}, + {"en_ID", "-1234.5", "-IDR 1,234.50", "-IDR 1,234.50", "(IDR 1,234.50)", "true"}, + {"en_ID@cf=account", "-1234.5", "(IDR 1,234.50)", "-IDR 1,234.50", "(IDR 1,234.50)", "true"}, + {"en_ID@cf=standard", "-1234.5", "-IDR 1,234.50", "-IDR 1,234.50", "(IDR 1,234.50)", "true"}, + {"sh_ME", "1234.5", "1.234,50 €", "1.234,50 €", "1.234,50 €", "true"}, + {"sh_ME@cf=account", "1234.5", "1.234,50 €", "1.234,50 €", "1.234,50 €", "true"}, + {"sh_ME@cf=standard", "1234.5", "1.234,50 €", "1.234,50 €", "1.234,50 €", "true"}, + {"sh_ME", "-1234.5", "-1.234,50 €", "-1.234,50 €", "(1.234,50 €)", "true"}, + {"sh_ME@cf=account", "-1234.5", "(1.234,50 €)", "-1.234,50 €", "(1.234,50 €)", "true"}, + {"sh_ME@cf=standard", "-1234.5", "-1.234,50 €", "-1.234,50 €", "(1.234,50 €)", "true"}, }; for (String[] data : tests) { ULocale loc = new ULocale(data[0]); @@ -4436,6 +4448,33 @@ public class NumberFormatTest extends TestFmwk { } } + /** + * en_ID/sh_ME uses language only locales en/sh which requires NumberFormatServiceShim to fill in the currency, but + * prior to ICU-20631, currency was not filled in for accounting, cash and standard, so currency placeholder was + * used instead of the desired locale's currency. + */ + @Test + public void TestCurrencyFormatForMissingLocale() { + ULocale loc = new ULocale("sh_ME"); + Currency cur = Currency.getInstance(loc); + NumberFormat curFmt = NumberFormat.getInstance(loc, NumberFormat.CURRENCYSTYLE); + assertNotNull("NumberFormat is missing currency instance for CURRENCYSTYLE", curFmt.getCurrency()); + assertEquals("Currency instance is not for the desired locale for CURRENCYSTYLE", cur, curFmt.getCurrency()); + assertEquals("NumberFormat format outputs wrong value for CURRENCYSTYLE", "-1.234,50 €", curFmt.format(-1234.5d)); + NumberFormat accFmt = NumberFormat.getInstance(loc, NumberFormat.ACCOUNTINGCURRENCYSTYLE); + assertNotNull("NumberFormat is missing currency instance for ACCOUNTINGCURRENCYSTYLE", accFmt.getCurrency()); + assertEquals("Currency instance is not for the desired locale for ACCOUNTINGCURRENCYSTYLE", cur, accFmt.getCurrency()); + assertEquals("NumberFormat format outputs wrong value for ACCOUNTINGCURRENCYSTYLE", "(1.234,50 €)", accFmt.format(-1234.5d)); + NumberFormat cashFmt = NumberFormat.getInstance(loc, NumberFormat.CASHCURRENCYSTYLE); + assertNotNull("NumberFormat is missing currency instance for CASHCURRENCYSTYLE", cashFmt.getCurrency()); + assertEquals("Currency instance is not for the desired locale for CASHCURRENCYSTYLE", cur, cashFmt.getCurrency()); + assertEquals("NumberFormat format outputs wrong value for CASHCURRENCYSTYLE", "-1.234,50 €", cashFmt.format(-1234.5d)); + NumberFormat stdFmt = NumberFormat.getInstance(loc, NumberFormat.STANDARDCURRENCYSTYLE); + assertNotNull("NumberFormat is missing currency instance for STANDARDCURRENCYSTYLE", stdFmt.getCurrency()); + assertEquals("Currency instance is not for the desired locale for STANDARDCURRENCYSTYLE", cur, stdFmt.getCurrency()); + assertEquals("NumberFormat format outputs wrong value for STANDARDCURRENCYSTYLE", "-1.234,50 €", stdFmt.format(-1234.5d)); + } + @Test public void TestCurrencyUsage() { // the 1st one is checking setter/getter, while the 2nd one checks for getInstance