diff --git a/icu4c/source/data/rbnf/root.txt b/icu4c/source/data/rbnf/root.txt index 6db5ee9add6..c1b9cc2696f 100644 --- a/icu4c/source/data/rbnf/root.txt +++ b/icu4c/source/data/rbnf/root.txt @@ -661,6 +661,8 @@ root{ "%%tamil-thousands:", "0: =%tamil=;", "1000: <<\u0BF2[>>];", + "%zDefault:", + "0: =#,##0=;", } OrdinalRules{ "%digits-ordinal:", diff --git a/icu4c/source/i18n/numfmt.cpp b/icu4c/source/i18n/numfmt.cpp index 8c90fdd5b76..0adad07379c 100644 --- a/icu4c/source/i18n/numfmt.cpp +++ b/icu4c/source/i18n/numfmt.cpp @@ -117,7 +117,7 @@ static const UChar * const gLastResortNumberPatterns[UNUM_FORMAT_STYLE_COUNT] = NULL, // UNUM_SPELLOUT NULL, // UNUM_ORDINAL NULL, // UNUM_DURATION - NULL, // UNUM_NUMBERING_SYSTEM + gLastResortDecimalPat, // UNUM_NUMBERING_SYSTEM NULL, // UNUM_PATTERN_RULEBASED gLastResortIsoCurrencyPat, // UNUM_CURRENCY_ISO gLastResortPluralCurrencyPat, // UNUM_CURRENCY_PLURAL @@ -1310,6 +1310,14 @@ NumberFormat::makeInstance(const Locale& desiredLocale, status = U_ILLEGAL_ARGUMENT_ERROR; return NULL; } + + // For the purposes of general number formatting, UNUM_NUMBERING_SYSTEM should behave the same + // was as UNUM_DECIMAL. In both cases, you get either a DecimalFormat or a RuleBasedNumberFormat + // depending on the locale's numbering system (either the default one for the locale or a specific + // one specified by using the "@numbers=" or "-u-nu-" parameter in the locale ID. + if (style == UNUM_NUMBERING_SYSTEM) { + style = UNUM_DECIMAL; + } // Some styles are not supported. This is a result of merging // the @draft ICU 4.2 NumberFormat::EStyles into the long-existing UNumberFormatStyle. diff --git a/icu4c/source/i18n/unicode/rbnf.h b/icu4c/source/i18n/unicode/rbnf.h index d4d7e225181..70d92dd549d 100644 --- a/icu4c/source/i18n/unicode/rbnf.h +++ b/icu4c/source/i18n/unicode/rbnf.h @@ -54,9 +54,27 @@ class RuleBasedCollator; * @stable ICU 2.2 */ enum URBNFRuleSetTag { + /** + * Requests predefined ruleset for spelling out numeric values in words. + * @stable ICU 2.2 + */ URBNF_SPELLOUT, + /** + * Requests predefined ruleset for the ordinal form of a number. + * @stable ICU 2.2 + */ URBNF_ORDINAL, + /** + * Requests predefined ruleset for formatting a value as a duration in hours, minutes, and seconds. + * @stable ICU 2.2 + */ URBNF_DURATION, + /** + * Requests predefined ruleset for various non-place-value numbering systems. + * WARNING: The same resource contains rule sets for a variety of different numbering systems. + * You need to call setDefaultRuleSet() on the formatter to choose the actual numbering system. + * @stable ICU 2.2 + */ URBNF_NUMBERING_SYSTEM, #ifndef U_HIDE_DEPRECATED_API /** @@ -662,6 +680,9 @@ public: * URBNF_DURATION, which formats a duration in seconds as hours, minutes, and seconds always rounding down, * and URBNF_NUMBERING_SYSTEM, which is used to invoke rules for alternate numbering * systems such as the Hebrew numbering system, or for Roman Numerals, etc. + * NOTE: If you use URBNF_NUMBERING_SYSTEM, you must also call setDefaultRuleSet() to + * specify the exact numbering system you want to use. If you want the default numbering system + * for the locale, call NumberFormat::createInstance() instead of creating a RuleBasedNumberFormat directly. * @param locale The locale for the formatter. * @param status The status indicating whether the constructor succeeded. * @stable ICU 2.0 diff --git a/icu4c/source/i18n/unum.cpp b/icu4c/source/i18n/unum.cpp index 769d59290e7..d2ba7ae3a2d 100644 --- a/icu4c/source/i18n/unum.cpp +++ b/icu4c/source/i18n/unum.cpp @@ -60,6 +60,7 @@ unum_open( UNumberFormatStyle style, case UNUM_CURRENCY_ACCOUNTING: case UNUM_CASH_CURRENCY: case UNUM_CURRENCY_STANDARD: + case UNUM_NUMBERING_SYSTEM: retVal = NumberFormat::createInstance(Locale(locale), style, *status); break; @@ -112,10 +113,6 @@ unum_open( UNumberFormatStyle style, case UNUM_DURATION: retVal = new RuleBasedNumberFormat(URBNF_DURATION, Locale(locale), *status); break; - - case UNUM_NUMBERING_SYSTEM: - retVal = new RuleBasedNumberFormat(URBNF_NUMBERING_SYSTEM, Locale(locale), *status); - break; #endif case UNUM_DECIMAL_COMPACT_SHORT: diff --git a/icu4c/source/test/cintltst/cnumtst.c b/icu4c/source/test/cintltst/cnumtst.c index e80e021fe86..fd9abe3a943 100644 --- a/icu4c/source/test/cintltst/cnumtst.c +++ b/icu4c/source/test/cintltst/cnumtst.c @@ -76,6 +76,7 @@ static void TestIgnorePadding(void); static void TestSciNotationMaxFracCap(void); static void TestMinIntMinFracZero(void); static void Test21479_ExactCurrency(void); +static void Test22088_Ethiopic(void); #define TESTCASE(x) addTest(root, &x, "tsformat/cnumtst/" #x) @@ -118,6 +119,7 @@ void addNumForTest(TestNode** root) TESTCASE(TestSciNotationMaxFracCap); TESTCASE(TestMinIntMinFracZero); TESTCASE(Test21479_ExactCurrency); + TESTCASE(Test22088_Ethiopic); } /* test Parse int 64 */ @@ -3605,4 +3607,30 @@ static void Test21479_ExactCurrency(void) { unum_close(nf); } +static void Test22088_Ethiopic(void) { + UErrorCode err = U_ZERO_ERROR; + UNumberFormat* nf1 = unum_open(UNUM_DEFAULT, NULL, 0, "am_ET@numbers=ethi", NULL, &err); + UNumberFormat* nf2 = unum_open(UNUM_NUMBERING_SYSTEM, NULL, 0, "am_ET@numbers=ethi", NULL, &err); + UNumberFormat* nf3 = unum_open(UNUM_NUMBERING_SYSTEM, NULL, 0, "en_US", NULL, &err); + + if (assertSuccess("Creation of number formatters failed", &err)) { + UChar result[200]; + + unum_formatDouble(nf1, 123, result, 200, NULL, &err); + assertSuccess("Formatting of number failed", &err); + assertUEquals("Wrong result with UNUM_DEFAULT", u"፻፳፫", result); + + unum_formatDouble(nf2, 123, result, 200, NULL, &err); + assertSuccess("Formatting of number failed", &err); + assertUEquals("Wrong result with UNUM_NUMBERING_SYSTEM", u"፻፳፫", result); + + unum_formatDouble(nf3, 123, result, 200, NULL, &err); + assertSuccess("Formatting of number failed", &err); + assertUEquals("Wrong result with UNUM_NUMBERING_SYSTEM and English", u"123", result); + } + unum_close(nf1); + unum_close(nf2); + unum_close(nf3); +} + #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/test/intltest/itrbnf.cpp b/icu4c/source/test/intltest/itrbnf.cpp index 847b8412840..672b73ddd66 100644 --- a/icu4c/source/test/intltest/itrbnf.cpp +++ b/icu4c/source/test/intltest/itrbnf.cpp @@ -78,6 +78,7 @@ void IntlTestRBNF::runIndexedTest(int32_t index, UBool exec, const char* &name, TESTCASE(26, TestParseFailure); TESTCASE(27, TestMinMaxIntegerDigitsIgnored); TESTCASE(28, TestNorwegianSpellout); + TESTCASE(29, TestNumberingSystem); #else TESTCASE(0, TestRBNFDisabled); #endif @@ -2489,6 +2490,21 @@ IntlTestRBNF::doLenientParseTest(RuleBasedNumberFormat* formatter, const char* t } } +void +IntlTestRBNF::TestNumberingSystem() { + IcuTestErrorCode err(*this, "TestNumberingSystem"); + RuleBasedNumberFormat rbnf(URBNF_NUMBERING_SYSTEM, Locale::getUS(), err); + + if (!err.errIfFailureAndReset("Failed to create RBNF with URBNF_NUMBERING_SYSTEM")) { + UnicodeString result; + assertEquals("Wrong result with default rule set", u"123", rbnf.format(123, result, err)); + + result.remove(); + rbnf.setDefaultRuleSet(u"%ethiopic", err); + assertEquals("Wrong result with Ethiopic rule set", u"፻፳፫", rbnf.format(123, result, err)); + } +} + /* U_HAVE_RBNF */ #else diff --git a/icu4c/source/test/intltest/itrbnf.h b/icu4c/source/test/intltest/itrbnf.h index 9fae1755cf1..96147ea224d 100644 --- a/icu4c/source/test/intltest/itrbnf.h +++ b/icu4c/source/test/intltest/itrbnf.h @@ -154,6 +154,7 @@ class IntlTestRBNF : public IntlTest { void TestCompactDecimalFormatStyle(); void TestParseFailure(); void TestMinMaxIntegerDigitsIgnored(); + void TestNumberingSystem(); protected: virtual void doTest(RuleBasedNumberFormat* formatter, const char* const testData[][2], UBool testParsing); diff --git a/icu4c/source/test/intltest/numfmtst.cpp b/icu4c/source/test/intltest/numfmtst.cpp index 1ed8cb2d410..3c5ec751214 100644 --- a/icu4c/source/test/intltest/numfmtst.cpp +++ b/icu4c/source/test/intltest/numfmtst.cpp @@ -252,6 +252,7 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n TESTCASE_AUTO(Test21232_ParseTimeout); TESTCASE_AUTO(Test10997_FormatCurrency); TESTCASE_AUTO(Test21556_CurrencyAsDecimal); + TESTCASE_AUTO(Test22088_Ethiopic); TESTCASE_AUTO_END; } @@ -10145,4 +10146,20 @@ void NumberFormatTest::Test21556_CurrencyAsDecimal() { } } +void NumberFormatTest::Test22088_Ethiopic() { + IcuTestErrorCode err(*this, "Test22088_Ethiopic"); + LocalPointer nf1(NumberFormat::createInstance(Locale("am_ET@numbers=ethi"), UNUM_DEFAULT, err)); + LocalPointer nf2(NumberFormat::createInstance(Locale("am_ET@numbers=ethi"), UNUM_NUMBERING_SYSTEM, err)); + LocalPointer nf3(NumberFormat::createInstance(Locale::getUS(), UNUM_NUMBERING_SYSTEM, err)); + + if (!err.errIfFailureAndReset("Creation of number formatters failed")) { + UnicodeString result; + assertEquals("Wrong result with UNUM_DEFAULT", u"፻፳፫", nf1->format(123, result)); + result.remove(); + assertEquals("Wrong result with UNUM_NUMBERING_SYSTEM", u"፻፳፫", nf2->format(123, result)); + result.remove(); + assertEquals("Wrong result with UNUM_NUMBERING_SYSTEM and English", u"123", nf3->format(123, result)); + } +} + #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/test/intltest/numfmtst.h b/icu4c/source/test/intltest/numfmtst.h index abd828d644b..526e90cd367 100644 --- a/icu4c/source/test/intltest/numfmtst.h +++ b/icu4c/source/test/intltest/numfmtst.h @@ -308,6 +308,7 @@ class NumberFormatTest: public CalendarTimeZoneTest { void Test21232_ParseTimeout(); void Test10997_FormatCurrency(); void Test21556_CurrencyAsDecimal(); + void Test22088_Ethiopic(); private: UBool testFormattableAsUFormattable(const char *file, int line, Formattable &f); diff --git a/icu4j/main/shared/data/icudata.jar b/icu4j/main/shared/data/icudata.jar index 533224a0798..40f7742b28d 100644 --- a/icu4j/main/shared/data/icudata.jar +++ b/icu4j/main/shared/data/icudata.jar @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3112f5cdf386100bc201a1fabc3a15a190d662d100712619a83ca211639d0fb -size 13891395 +oid sha256:7280fe2b5614523890959916529e198ee81e51dcdbc02d61882c0257f3346e8d +size 13891411 diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/RbnfTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/RbnfTest.java index e7973255982..2ebafe2b522 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/RbnfTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/RbnfTest.java @@ -1839,4 +1839,14 @@ public class RbnfTest extends TestFmwk { assertEquals("infinity", rbnf.format(Double.POSITIVE_INFINITY)); assertEquals("not a number", rbnf.format(Double.NaN)); } + + @Test + public void TestNumberingSystem() { + RuleBasedNumberFormat rbnf = new RuleBasedNumberFormat(ULocale.US, RuleBasedNumberFormat.NUMBERING_SYSTEM); + + assertEquals("Wrong result with default rule set", "123", rbnf.format(123)); + + rbnf.setDefaultRuleSet("%ethiopic"); + assertEquals("Wrong result with Ethiopic rule set", "፻፳፫", rbnf.format(123)); + } }