| // © 2017 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| |
| #include "unicode/utypes.h" |
| |
| #if !UCONFIG_NO_FORMATTING |
| |
| #include "charstr.h" |
| #include <cstdarg> |
| #include <cmath> |
| #include <memory> |
| #include "unicode/unum.h" |
| #include "unicode/numberformatter.h" |
| #include "unicode/testlog.h" |
| #include "unicode/utypes.h" |
| #include "number_asformat.h" |
| #include "number_types.h" |
| #include "number_utils.h" |
| #include "number_utypes.h" |
| #include "number_microprops.h" |
| #include "numbertest.h" |
| |
| using number::impl::UFormattedNumberData; |
| |
| // Horrible workaround for the lack of a status code in the constructor... |
| // (Also affects numbertest_range.cpp) |
| UErrorCode globalNumberFormatterApiTestStatus = U_ZERO_ERROR; |
| |
| NumberFormatterApiTest::NumberFormatterApiTest() |
| : NumberFormatterApiTest(globalNumberFormatterApiTestStatus) { |
| } |
| |
| NumberFormatterApiTest::NumberFormatterApiTest(UErrorCode& status) |
| : USD(u"USD", status), |
| GBP(u"GBP", status), |
| CZK(u"CZK", status), |
| CAD(u"CAD", status), |
| ESP(u"ESP", status), |
| PTE(u"PTE", status), |
| RON(u"RON", status), |
| TWD(u"TWD", status), |
| TRY(u"TRY", status), |
| CNY(u"CNY", status), |
| FRENCH_SYMBOLS(Locale::getFrench(), status), |
| SWISS_SYMBOLS(Locale("de-CH"), status), |
| MYANMAR_SYMBOLS(Locale("my"), status) { |
| |
| // Check for error on the first MeasureUnit in case there is no data |
| LocalPointer<MeasureUnit> unit(MeasureUnit::createMeter(status)); |
| if (U_FAILURE(status)) { |
| dataerrln("%s %d status = %s", __FILE__, __LINE__, u_errorName(status)); |
| return; |
| } |
| METER = *unit; |
| |
| METER_PER_SECOND = *LocalPointer<MeasureUnit>(MeasureUnit::createMeterPerSecond(status)); |
| DAY = *LocalPointer<MeasureUnit>(MeasureUnit::createDay(status)); |
| SQUARE_METER = *LocalPointer<MeasureUnit>(MeasureUnit::createSquareMeter(status)); |
| FAHRENHEIT = *LocalPointer<MeasureUnit>(MeasureUnit::createFahrenheit(status)); |
| SECOND = *LocalPointer<MeasureUnit>(MeasureUnit::createSecond(status)); |
| POUND = *LocalPointer<MeasureUnit>(MeasureUnit::createPound(status)); |
| POUND_FORCE = *LocalPointer<MeasureUnit>(MeasureUnit::createPoundForce(status)); |
| SQUARE_MILE = *LocalPointer<MeasureUnit>(MeasureUnit::createSquareMile(status)); |
| SQUARE_INCH = *LocalPointer<MeasureUnit>(MeasureUnit::createSquareInch(status)); |
| JOULE = *LocalPointer<MeasureUnit>(MeasureUnit::createJoule(status)); |
| FURLONG = *LocalPointer<MeasureUnit>(MeasureUnit::createFurlong(status)); |
| KELVIN = *LocalPointer<MeasureUnit>(MeasureUnit::createKelvin(status)); |
| |
| MATHSANB = *LocalPointer<NumberingSystem>(NumberingSystem::createInstanceByName("mathsanb", status)); |
| LATN = *LocalPointer<NumberingSystem>(NumberingSystem::createInstanceByName("latn", status)); |
| } |
| |
| void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const char*& name, char*) { |
| if (exec) { |
| logln("TestSuite NumberFormatterApiTest: "); |
| } |
| TESTCASE_AUTO_BEGIN; |
| TESTCASE_AUTO(notationSimple); |
| TESTCASE_AUTO(notationScientific); |
| TESTCASE_AUTO(notationCompact); |
| TESTCASE_AUTO(unitMeasure); |
| TESTCASE_AUTO(unitCompoundMeasure); |
| TESTCASE_AUTO(unitArbitraryMeasureUnits); |
| TESTCASE_AUTO(unitSkeletons); |
| TESTCASE_AUTO(unitUsage); |
| TESTCASE_AUTO(unitUsageErrorCodes); |
| TESTCASE_AUTO(unitUsageSkeletons); |
| TESTCASE_AUTO(unitCurrency); |
| TESTCASE_AUTO(unitInflections); |
| TESTCASE_AUTO(unitGender); |
| TESTCASE_AUTO(unitPercent); |
| if (!quick) { |
| // Slow test: run in exhaustive mode only |
| TESTCASE_AUTO(percentParity); |
| } |
| TESTCASE_AUTO(roundingFraction); |
| TESTCASE_AUTO(roundingFigures); |
| TESTCASE_AUTO(roundingFractionFigures); |
| TESTCASE_AUTO(roundingOther); |
| TESTCASE_AUTO(grouping); |
| TESTCASE_AUTO(padding); |
| TESTCASE_AUTO(integerWidth); |
| TESTCASE_AUTO(symbols); |
| // TODO: Add this method if currency symbols override support is added. |
| //TESTCASE_AUTO(symbolsOverride); |
| TESTCASE_AUTO(sign); |
| TESTCASE_AUTO(signNearZero); |
| TESTCASE_AUTO(signCoverage); |
| TESTCASE_AUTO(decimal); |
| TESTCASE_AUTO(scale); |
| TESTCASE_AUTO(locale); |
| TESTCASE_AUTO(skeletonUserGuideExamples); |
| TESTCASE_AUTO(formatTypes); |
| TESTCASE_AUTO(fieldPositionLogic); |
| TESTCASE_AUTO(fieldPositionCoverage); |
| TESTCASE_AUTO(toFormat); |
| TESTCASE_AUTO(errors); |
| if (!quick) { |
| // Slow test: run in exhaustive mode only |
| // (somewhat slow to check all permutations of settings) |
| TESTCASE_AUTO(validRanges); |
| } |
| TESTCASE_AUTO(copyMove); |
| TESTCASE_AUTO(localPointerCAPI); |
| TESTCASE_AUTO(toObject); |
| TESTCASE_AUTO(toDecimalNumber); |
| TESTCASE_AUTO(microPropsInternals); |
| TESTCASE_AUTO_END; |
| } |
| |
| void NumberFormatterApiTest::notationSimple() { |
| assertFormatDescending( |
| u"Basic", |
| u"", |
| u"", |
| NumberFormatter::with(), |
| Locale::getEnglish(), |
| u"87,650", |
| u"8,765", |
| u"876.5", |
| u"87.65", |
| u"8.765", |
| u"0.8765", |
| u"0.08765", |
| u"0.008765", |
| u"0"); |
| |
| assertFormatDescendingBig( |
| u"Big Simple", |
| u"notation-simple", |
| u"", |
| NumberFormatter::with().notation(Notation::simple()), |
| Locale::getEnglish(), |
| u"87,650,000", |
| u"8,765,000", |
| u"876,500", |
| u"87,650", |
| u"8,765", |
| u"876.5", |
| u"87.65", |
| u"8.765", |
| u"0"); |
| |
| assertFormatSingle( |
| u"Basic with Negative Sign", |
| u"", |
| u"", |
| NumberFormatter::with(), |
| Locale::getEnglish(), |
| -9876543.21, |
| u"-9,876,543.21"); |
| } |
| |
| |
| void NumberFormatterApiTest::notationScientific() { |
| assertFormatDescending( |
| u"Scientific", |
| u"scientific", |
| u"E0", |
| NumberFormatter::with().notation(Notation::scientific()), |
| Locale::getEnglish(), |
| u"8.765E4", |
| u"8.765E3", |
| u"8.765E2", |
| u"8.765E1", |
| u"8.765E0", |
| u"8.765E-1", |
| u"8.765E-2", |
| u"8.765E-3", |
| u"0E0"); |
| |
| assertFormatDescending( |
| u"Engineering", |
| u"engineering", |
| u"EE0", |
| NumberFormatter::with().notation(Notation::engineering()), |
| Locale::getEnglish(), |
| u"87.65E3", |
| u"8.765E3", |
| u"876.5E0", |
| u"87.65E0", |
| u"8.765E0", |
| u"876.5E-3", |
| u"87.65E-3", |
| u"8.765E-3", |
| u"0E0"); |
| |
| assertFormatDescending( |
| u"Scientific sign always shown", |
| u"scientific/sign-always", |
| u"E+!0", |
| NumberFormatter::with().notation( |
| Notation::scientific().withExponentSignDisplay(UNumberSignDisplay::UNUM_SIGN_ALWAYS)), |
| Locale::getEnglish(), |
| u"8.765E+4", |
| u"8.765E+3", |
| u"8.765E+2", |
| u"8.765E+1", |
| u"8.765E+0", |
| u"8.765E-1", |
| u"8.765E-2", |
| u"8.765E-3", |
| u"0E+0"); |
| |
| assertFormatDescending( |
| u"Scientific min exponent digits", |
| u"scientific/*ee", |
| u"E00", |
| NumberFormatter::with().notation(Notation::scientific().withMinExponentDigits(2)), |
| Locale::getEnglish(), |
| u"8.765E04", |
| u"8.765E03", |
| u"8.765E02", |
| u"8.765E01", |
| u"8.765E00", |
| u"8.765E-01", |
| u"8.765E-02", |
| u"8.765E-03", |
| u"0E00"); |
| |
| assertFormatSingle( |
| u"Scientific Negative", |
| u"scientific", |
| u"E0", |
| NumberFormatter::with().notation(Notation::scientific()), |
| Locale::getEnglish(), |
| -1000000, |
| u"-1E6"); |
| |
| assertFormatSingle( |
| u"Scientific Infinity", |
| u"scientific", |
| u"E0", |
| NumberFormatter::with().notation(Notation::scientific()), |
| Locale::getEnglish(), |
| -uprv_getInfinity(), |
| u"-∞"); |
| |
| assertFormatSingle( |
| u"Scientific NaN", |
| u"scientific", |
| u"E0", |
| NumberFormatter::with().notation(Notation::scientific()), |
| Locale::getEnglish(), |
| uprv_getNaN(), |
| u"NaN"); |
| } |
| |
| void NumberFormatterApiTest::notationCompact() { |
| assertFormatDescending( |
| u"Compact Short", |
| u"compact-short", |
| u"K", |
| NumberFormatter::with().notation(Notation::compactShort()), |
| Locale::getEnglish(), |
| u"88K", |
| u"8.8K", |
| u"876", |
| u"88", |
| u"8.8", |
| u"0.88", |
| u"0.088", |
| u"0.0088", |
| u"0"); |
| |
| assertFormatDescending( |
| u"Compact Long", |
| u"compact-long", |
| u"KK", |
| NumberFormatter::with().notation(Notation::compactLong()), |
| Locale::getEnglish(), |
| u"88 thousand", |
| u"8.8 thousand", |
| u"876", |
| u"88", |
| u"8.8", |
| u"0.88", |
| u"0.088", |
| u"0.0088", |
| u"0"); |
| |
| assertFormatDescending( |
| u"Compact Short Currency", |
| u"compact-short currency/USD", |
| u"K currency/USD", |
| NumberFormatter::with().notation(Notation::compactShort()).unit(USD), |
| Locale::getEnglish(), |
| u"$88K", |
| u"$8.8K", |
| u"$876", |
| u"$88", |
| u"$8.8", |
| u"$0.88", |
| u"$0.088", |
| u"$0.0088", |
| u"$0"); |
| |
| assertFormatDescending( |
| u"Compact Short with ISO Currency", |
| u"compact-short currency/USD unit-width-iso-code", |
| u"K currency/USD unit-width-iso-code", |
| NumberFormatter::with().notation(Notation::compactShort()) |
| .unit(USD) |
| .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE), |
| Locale::getEnglish(), |
| u"USD 88K", |
| u"USD 8.8K", |
| u"USD 876", |
| u"USD 88", |
| u"USD 8.8", |
| u"USD 0.88", |
| u"USD 0.088", |
| u"USD 0.0088", |
| u"USD 0"); |
| |
| assertFormatDescending( |
| u"Compact Short with Long Name Currency", |
| u"compact-short currency/USD unit-width-full-name", |
| u"K currency/USD unit-width-full-name", |
| NumberFormatter::with().notation(Notation::compactShort()) |
| .unit(USD) |
| .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale::getEnglish(), |
| u"88K US dollars", |
| u"8.8K US dollars", |
| u"876 US dollars", |
| u"88 US dollars", |
| u"8.8 US dollars", |
| u"0.88 US dollars", |
| u"0.088 US dollars", |
| u"0.0088 US dollars", |
| u"0 US dollars"); |
| |
| // Note: Most locales don't have compact long currency, so this currently falls back to short. |
| // This test case should be fixed when proper compact long currency patterns are added. |
| assertFormatDescending( |
| u"Compact Long Currency", |
| u"compact-long currency/USD", |
| u"KK currency/USD", |
| NumberFormatter::with().notation(Notation::compactLong()).unit(USD), |
| Locale::getEnglish(), |
| u"$88K", // should be something like "$88 thousand" |
| u"$8.8K", |
| u"$876", |
| u"$88", |
| u"$8.8", |
| u"$0.88", |
| u"$0.088", |
| u"$0.0088", |
| u"$0"); |
| |
| // Note: Most locales don't have compact long currency, so this currently falls back to short. |
| // This test case should be fixed when proper compact long currency patterns are added. |
| assertFormatDescending( |
| u"Compact Long with ISO Currency", |
| u"compact-long currency/USD unit-width-iso-code", |
| u"KK currency/USD unit-width-iso-code", |
| NumberFormatter::with().notation(Notation::compactLong()) |
| .unit(USD) |
| .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE), |
| Locale::getEnglish(), |
| u"USD 88K", // should be something like "USD 88 thousand" |
| u"USD 8.8K", |
| u"USD 876", |
| u"USD 88", |
| u"USD 8.8", |
| u"USD 0.88", |
| u"USD 0.088", |
| u"USD 0.0088", |
| u"USD 0"); |
| |
| // TODO: This behavior could be improved and should be revisited. |
| assertFormatDescending( |
| u"Compact Long with Long Name Currency", |
| u"compact-long currency/USD unit-width-full-name", |
| u"KK currency/USD unit-width-full-name", |
| NumberFormatter::with().notation(Notation::compactLong()) |
| .unit(USD) |
| .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale::getEnglish(), |
| u"88 thousand US dollars", |
| u"8.8 thousand US dollars", |
| u"876 US dollars", |
| u"88 US dollars", |
| u"8.8 US dollars", |
| u"0.88 US dollars", |
| u"0.088 US dollars", |
| u"0.0088 US dollars", |
| u"0 US dollars"); |
| |
| assertFormatSingle( |
| u"Compact Plural One", |
| u"compact-long", |
| u"KK", |
| NumberFormatter::with().notation(Notation::compactLong()), |
| Locale::createFromName("es"), |
| 1000000, |
| u"1 millón"); |
| |
| assertFormatSingle( |
| u"Compact Plural Other", |
| u"compact-long", |
| u"KK", |
| NumberFormatter::with().notation(Notation::compactLong()), |
| Locale::createFromName("es"), |
| 2000000, |
| u"2 millones"); |
| |
| assertFormatSingle( |
| u"Compact with Negative Sign", |
| u"compact-short", |
| u"K", |
| NumberFormatter::with().notation(Notation::compactShort()), |
| Locale::getEnglish(), |
| -9876543.21, |
| u"-9.9M"); |
| |
| assertFormatSingle( |
| u"Compact Rounding", |
| u"compact-short", |
| u"K", |
| NumberFormatter::with().notation(Notation::compactShort()), |
| Locale::getEnglish(), |
| 990000, |
| u"990K"); |
| |
| assertFormatSingle( |
| u"Compact Rounding", |
| u"compact-short", |
| u"K", |
| NumberFormatter::with().notation(Notation::compactShort()), |
| Locale::getEnglish(), |
| 999000, |
| u"999K"); |
| |
| assertFormatSingle( |
| u"Compact Rounding", |
| u"compact-short", |
| u"K", |
| NumberFormatter::with().notation(Notation::compactShort()), |
| Locale::getEnglish(), |
| 999900, |
| u"1M"); |
| |
| assertFormatSingle( |
| u"Compact Rounding", |
| u"compact-short", |
| u"K", |
| NumberFormatter::with().notation(Notation::compactShort()), |
| Locale::getEnglish(), |
| 9900000, |
| u"9.9M"); |
| |
| assertFormatSingle( |
| u"Compact Rounding", |
| u"compact-short", |
| u"K", |
| NumberFormatter::with().notation(Notation::compactShort()), |
| Locale::getEnglish(), |
| 9990000, |
| u"10M"); |
| |
| assertFormatSingle( |
| u"Compact in zh-Hant-HK", |
| u"compact-short", |
| u"K", |
| NumberFormatter::with().notation(Notation::compactShort()), |
| Locale("zh-Hant-HK"), |
| 1e7, |
| u"10M"); |
| |
| assertFormatSingle( |
| u"Compact in zh-Hant", |
| u"compact-short", |
| u"K", |
| NumberFormatter::with().notation(Notation::compactShort()), |
| Locale("zh-Hant"), |
| 1e7, |
| u"1000\u842C"); |
| |
| if (!logKnownIssue("21258", "StandardPlural cannot handle keywords 1, 0")) { |
| assertFormatSingle( |
| u"Compact with plural form =1 (ICU-21258)", |
| u"compact-long", |
| u"K", |
| NumberFormatter::with().notation(Notation::compactLong()), |
| Locale("fr-FR"), |
| 1e3, |
| u"mille"); |
| } |
| |
| assertFormatSingle( |
| u"Compact Infinity", |
| u"compact-short", |
| u"K", |
| NumberFormatter::with().notation(Notation::compactShort()), |
| Locale::getEnglish(), |
| -uprv_getInfinity(), |
| u"-∞"); |
| |
| assertFormatSingle( |
| u"Compact NaN", |
| u"compact-short", |
| u"K", |
| NumberFormatter::with().notation(Notation::compactShort()), |
| Locale::getEnglish(), |
| uprv_getNaN(), |
| u"NaN"); |
| |
| // NOTE: There is no API for compact custom data in C++ |
| // and thus no "Compact Somali No Figure" test |
| } |
| |
| void NumberFormatterApiTest::unitMeasure() { |
| IcuTestErrorCode status(*this, "unitMeasure()"); |
| |
| assertFormatDescending( |
| u"Meters Short and unit() method", |
| u"measure-unit/length-meter", |
| u"unit/meter", |
| NumberFormatter::with().unit(MeasureUnit::getMeter()), |
| Locale::getEnglish(), |
| u"87,650 m", |
| u"8,765 m", |
| u"876.5 m", |
| u"87.65 m", |
| u"8.765 m", |
| u"0.8765 m", |
| u"0.08765 m", |
| u"0.008765 m", |
| u"0 m"); |
| |
| assertFormatDescending( |
| u"Meters Long and adoptUnit() method", |
| u"measure-unit/length-meter unit-width-full-name", |
| u"unit/meter unit-width-full-name", |
| NumberFormatter::with().adoptUnit(new MeasureUnit(METER)) |
| .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale::getEnglish(), |
| u"87,650 meters", |
| u"8,765 meters", |
| u"876.5 meters", |
| u"87.65 meters", |
| u"8.765 meters", |
| u"0.8765 meters", |
| u"0.08765 meters", |
| u"0.008765 meters", |
| u"0 meters"); |
| |
| assertFormatDescending( |
| u"Compact Meters Long", |
| u"compact-long measure-unit/length-meter unit-width-full-name", |
| u"KK unit/meter unit-width-full-name", |
| NumberFormatter::with().notation(Notation::compactLong()) |
| .unit(METER) |
| .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale::getEnglish(), |
| u"88 thousand meters", |
| u"8.8 thousand meters", |
| u"876 meters", |
| u"88 meters", |
| u"8.8 meters", |
| u"0.88 meters", |
| u"0.088 meters", |
| u"0.0088 meters", |
| u"0 meters"); |
| |
| assertFormatDescending( |
| u"Hectometers", |
| u"unit/hectometer", |
| u"unit/hectometer", |
| NumberFormatter::with().unit(MeasureUnit::forIdentifier("hectometer", status)), |
| Locale::getEnglish(), |
| u"87,650 hm", |
| u"8,765 hm", |
| u"876.5 hm", |
| u"87.65 hm", |
| u"8.765 hm", |
| u"0.8765 hm", |
| u"0.08765 hm", |
| u"0.008765 hm", |
| u"0 hm"); |
| |
| // TODO: Implement Measure in C++ |
| // assertFormatSingleMeasure( |
| // u"Meters with Measure Input", |
| // NumberFormatter::with().unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME), |
| // Locale::getEnglish(), |
| // new Measure(5.43, new MeasureUnit(METER)), |
| // u"5.43 meters"); |
| |
| // TODO: Implement Measure in C++ |
| // assertFormatSingleMeasure( |
| // u"Measure format method takes precedence over fluent chain", |
| // NumberFormatter::with().unit(METER), |
| // Locale::getEnglish(), |
| // new Measure(5.43, USD), |
| // u"$5.43"); |
| |
| assertFormatSingle( |
| u"Meters with Negative Sign", |
| u"measure-unit/length-meter", |
| u"unit/meter", |
| NumberFormatter::with().unit(METER), |
| Locale::getEnglish(), |
| -9876543.21, |
| u"-9,876,543.21 m"); |
| |
| // The locale string "सान" appears only in brx.txt: |
| assertFormatSingle( |
| u"Interesting Data Fallback 1", |
| u"measure-unit/duration-day unit-width-full-name", |
| u"unit/day unit-width-full-name", |
| NumberFormatter::with().unit(DAY).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale::createFromName("brx"), |
| 5.43, |
| u"5.43 सान"); |
| |
| // Requires following the alias from unitsNarrow to unitsShort: |
| assertFormatSingle( |
| u"Interesting Data Fallback 2", |
| u"measure-unit/duration-day unit-width-narrow", |
| u"unit/day unit-width-narrow", |
| NumberFormatter::with().unit(DAY).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW), |
| Locale::createFromName("brx"), |
| 5.43, |
| u"5.43 d"); |
| |
| // en_001.txt has a unitsNarrow/area/square-meter table, but table does not contain the OTHER unit, |
| // requiring fallback to the root. |
| assertFormatSingle( |
| u"Interesting Data Fallback 3", |
| u"measure-unit/area-square-meter unit-width-narrow", |
| u"unit/square-meter unit-width-narrow", |
| NumberFormatter::with().unit(SQUARE_METER).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW), |
| Locale::createFromName("en-GB"), |
| 5.43, |
| u"5.43m²"); |
| |
| // Try accessing a narrow unit directly from root. |
| assertFormatSingle( |
| u"Interesting Data Fallback 4", |
| u"measure-unit/area-square-meter unit-width-narrow", |
| u"unit/square-meter unit-width-narrow", |
| NumberFormatter::with().unit(SQUARE_METER).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW), |
| Locale::createFromName("root"), |
| 5.43, |
| u"5.43 m²"); |
| |
| // es_US has "{0}°" for unitsNarrow/temperature/FAHRENHEIT. |
| // NOTE: This example is in the documentation. |
| assertFormatSingle( |
| u"Difference between Narrow and Short (Narrow Version)", |
| u"measure-unit/temperature-fahrenheit unit-width-narrow", |
| u"unit/fahrenheit unit-width-narrow", |
| NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_NARROW), |
| Locale("es-US"), |
| 5.43, |
| u"5.43°"); |
| |
| assertFormatSingle( |
| u"Difference between Narrow and Short (Short Version)", |
| u"measure-unit/temperature-fahrenheit unit-width-short", |
| u"unit/fahrenheit unit-width-short", |
| NumberFormatter::with().unit(FAHRENHEIT).unitWidth(UNUM_UNIT_WIDTH_SHORT), |
| Locale("es-US"), |
| 5.43, |
| u"5.43 °F"); |
| |
| assertFormatSingle( |
| u"MeasureUnit form without {0} in CLDR pattern", |
| u"measure-unit/temperature-kelvin unit-width-full-name", |
| u"unit/kelvin unit-width-full-name", |
| NumberFormatter::with().unit(KELVIN).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("es-MX"), |
| 1, |
| u"kelvin"); |
| |
| assertFormatSingle( |
| u"MeasureUnit form without {0} in CLDR pattern and wide base form", |
| u"measure-unit/temperature-kelvin .00000000000000000000 unit-width-full-name", |
| u"unit/kelvin .00000000000000000000 unit-width-full-name", |
| NumberFormatter::with().precision(Precision::fixedFraction(20)) |
| .unit(KELVIN) |
| .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("es-MX"), |
| 1, |
| u"kelvin"); |
| |
| assertFormatSingle( |
| u"Person unit not in short form", |
| u"measure-unit/duration-year-person unit-width-full-name", |
| u"unit/year-person unit-width-full-name", |
| NumberFormatter::with().unit(MeasureUnit::getYearPerson()) |
| .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("es-MX"), |
| 5, |
| u"5 a\u00F1os"); |
| |
| assertFormatSingle( |
| u"Hubble Constant - usually expressed in km/s/Mpc", |
| u"unit/kilometer-per-megaparsec-second", |
| u"unit/kilometer-per-megaparsec-second", |
| NumberFormatter::with().unit(MeasureUnit::forIdentifier("kilometer-per-second-per-megaparsec", status)), |
| Locale("en"), |
| 74, // Approximate 2019-03-18 measurement |
| u"74 km/Mpc⋅sec"); |
| |
| assertFormatSingle( |
| u"Mixed unit", |
| u"unit/yard-and-foot-and-inch", |
| u"unit/yard-and-foot-and-inch", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("yard-and-foot-and-inch", status)), |
| Locale("en-US"), |
| 3.65, |
| "3 yd, 1 ft, 11.4 in"); |
| |
| assertFormatSingle( |
| u"Mixed unit, Scientific", |
| u"unit/yard-and-foot-and-inch E0", |
| u"unit/yard-and-foot-and-inch E0", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("yard-and-foot-and-inch", status)) |
| .notation(Notation::scientific()), |
| Locale("en-US"), |
| 3.65, |
| "3 yd, 1 ft, 1.14E1 in"); |
| |
| assertFormatSingle( |
| u"Mixed Unit (Narrow Version)", |
| u"unit/metric-ton-and-kilogram-and-gram unit-width-narrow", |
| u"unit/metric-ton-and-kilogram-and-gram unit-width-narrow", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("metric-ton-and-kilogram-and-gram", status)) |
| .unitWidth(UNUM_UNIT_WIDTH_NARROW), |
| Locale("en-US"), |
| 4.28571, |
| u"4t 285kg 710g"); |
| |
| assertFormatSingle( |
| u"Mixed Unit (Short Version)", |
| u"unit/metric-ton-and-kilogram-and-gram unit-width-short", |
| u"unit/metric-ton-and-kilogram-and-gram unit-width-short", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("metric-ton-and-kilogram-and-gram", status)) |
| .unitWidth(UNUM_UNIT_WIDTH_SHORT), |
| Locale("en-US"), |
| 4.28571, |
| u"4 t, 285 kg, 710 g"); |
| |
| assertFormatSingle( |
| u"Mixed Unit (Full Name Version)", |
| u"unit/metric-ton-and-kilogram-and-gram unit-width-full-name", |
| u"unit/metric-ton-and-kilogram-and-gram unit-width-full-name", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("metric-ton-and-kilogram-and-gram", status)) |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("en-US"), |
| 4.28571, |
| u"4 metric tons, 285 kilograms, 710 grams"); |
| |
| assertFormatSingle(u"Mixed Unit (Not Sorted) [metric]", // |
| u"unit/gram-and-kilogram unit-width-full-name", // |
| u"unit/gram-and-kilogram unit-width-full-name", // |
| NumberFormatter::with() // |
| .unit(MeasureUnit::forIdentifier("gram-and-kilogram", status)) // |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), // |
| Locale("en-US"), // |
| 4.28571, // |
| u"285.71 grams, 4 kilograms"); // |
| |
| assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial]", // |
| u"unit/inch-and-yard-and-foot unit-width-full-name", // |
| u"unit/inch-and-yard-and-foot unit-width-full-name", // |
| NumberFormatter::with() // |
| .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) // |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), // |
| Locale("en-US"), // |
| 4.28571, // |
| u"10.28556 inches, 4 yards, 0 feet"); // |
| |
| assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial full]", // |
| u"unit/inch-and-yard-and-foot unit-width-full-name", // |
| u"unit/inch-and-yard-and-foot unit-width-full-name", // |
| NumberFormatter::with() // |
| .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) // |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), // |
| Locale("en-US"), // |
| 4.38571, // |
| u"1.88556 inches, 4 yards, 1 foot"); // |
| |
| assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial full integers]", // |
| u"unit/inch-and-yard-and-foot @# unit-width-full-name", // |
| u"unit/inch-and-yard-and-foot @# unit-width-full-name", // |
| NumberFormatter::with() // |
| .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) // |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME) // |
| .precision(Precision::maxSignificantDigits(2)), // |
| Locale("en-US"), // |
| 4.36112, // |
| u"1 inch, 4 yards, 1 foot"); // |
| |
| assertFormatSingle(u"Mixed Unit (Not Sorted) [imperial full] with `And` in the end", // |
| u"unit/inch-and-yard-and-foot unit-width-full-name", // |
| u"unit/inch-and-yard-and-foot unit-width-full-name", // |
| NumberFormatter::with() // |
| .unit(MeasureUnit::forIdentifier("inch-and-yard-and-foot", status)) // |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), // |
| Locale("fr-FR"), // |
| 4.38571, // |
| u"1,88556\u00A0pouce, 4\u00A0yards et 1\u00A0pied"); // |
| |
| assertFormatSingle(u"Mixed unit, Scientific [Not in Order]", // |
| u"unit/foot-and-inch-and-yard E0", // |
| u"unit/foot-and-inch-and-yard E0", // |
| NumberFormatter::with() // |
| .unit(MeasureUnit::forIdentifier("foot-and-inch-and-yard", status)) // |
| .notation(Notation::scientific()), // |
| Locale("en-US"), // |
| 3.65, // |
| "1 ft, 1.14E1 in, 3 yd"); // |
| |
| assertFormatSingle( |
| u"Testing \"1 foot 12 inches\"", |
| u"unit/foot-and-inch @### unit-width-full-name", |
| u"unit/foot-and-inch @### unit-width-full-name", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("foot-and-inch", status)) |
| .precision(Precision::maxSignificantDigits(4)) |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("en-US"), |
| 1.9999, |
| u"2 feet, 0 inches"); |
| |
| assertFormatSingle( |
| u"Negative numbers: temperature", |
| u"measure-unit/temperature-celsius", |
| u"unit/celsius", |
| NumberFormatter::with().unit(MeasureUnit::forIdentifier("celsius", status)), |
| Locale("nl-NL"), |
| -6.5, |
| u"-6,5°C"); |
| |
| assertFormatSingle( |
| u"Negative numbers: time", |
| u"unit/hour-and-minute-and-second", |
| u"unit/hour-and-minute-and-second", |
| NumberFormatter::with().unit(MeasureUnit::forIdentifier("hour-and-minute-and-second", status)), |
| Locale("de-DE"), |
| -1.24, |
| u"-1 Std., 14 Min. und 24 Sek."); |
| |
| assertFormatSingle( |
| u"Zero out the unit field", |
| u"", |
| u"", |
| NumberFormatter::with().unit(KELVIN).unit(MeasureUnit()), |
| Locale("en"), |
| 100, |
| u"100"); |
| |
| // TODO: desired behaviour for this "pathological" case? |
| // Since this is pointless, we don't test that its behaviour doesn't change. |
| // As of January 2021, the produced result has a missing sign: 23.5 Kelvin |
| // is "23 Kelvin and -272.65 degrees Celsius": |
| // assertFormatSingle( |
| // u"Meaningless: kelvin-and-celcius", |
| // u"unit/kelvin-and-celsius", |
| // u"unit/kelvin-and-celsius", |
| // NumberFormatter::with().unit(MeasureUnit::forIdentifier("kelvin-and-celsius", status)), |
| // Locale("en"), |
| // 23.5, |
| // u"23 K, 272.65°C"); |
| |
| if (uprv_getNaN() != 0.0) { |
| assertFormatSingle( |
| u"Measured -Inf", |
| u"measure-unit/electric-ampere", |
| u"unit/ampere", |
| NumberFormatter::with().unit(MeasureUnit::getAmpere()), |
| Locale("en"), |
| -uprv_getInfinity(), |
| u"-∞ A"); |
| |
| assertFormatSingle( |
| u"Measured NaN", |
| u"measure-unit/temperature-celsius", |
| u"unit/celsius", |
| NumberFormatter::with().unit(MeasureUnit::forIdentifier("celsius", status)), |
| Locale("en"), |
| uprv_getNaN(), |
| u"NaN°C"); |
| } |
| } |
| |
| void NumberFormatterApiTest::unitCompoundMeasure() { |
| IcuTestErrorCode status(*this, "unitCompoundMeasure()"); |
| |
| assertFormatDescending( |
| u"Meters Per Second Short (unit that simplifies) and perUnit method", |
| u"measure-unit/length-meter per-measure-unit/duration-second", |
| u"unit/meter-per-second", |
| NumberFormatter::with().unit(METER).perUnit(SECOND), |
| Locale::getEnglish(), |
| u"87,650 m/s", |
| u"8,765 m/s", |
| u"876.5 m/s", |
| u"87.65 m/s", |
| u"8.765 m/s", |
| u"0.8765 m/s", |
| u"0.08765 m/s", |
| u"0.008765 m/s", |
| u"0 m/s"); |
| |
| assertFormatDescending( |
| u"Meters Per Second Short, built-in m/s", |
| u"measure-unit/speed-meter-per-second", |
| u"unit/meter-per-second", |
| NumberFormatter::with().unit(METER_PER_SECOND), |
| Locale::getEnglish(), |
| u"87,650 m/s", |
| u"8,765 m/s", |
| u"876.5 m/s", |
| u"87.65 m/s", |
| u"8.765 m/s", |
| u"0.8765 m/s", |
| u"0.08765 m/s", |
| u"0.008765 m/s", |
| u"0 m/s"); |
| |
| assertFormatDescending( |
| u"Pounds Per Square Mile Short (secondary unit has per-format) and adoptPerUnit method", |
| u"measure-unit/mass-pound per-measure-unit/area-square-mile", |
| u"unit/pound-per-square-mile", |
| NumberFormatter::with().unit(POUND).adoptPerUnit(new MeasureUnit(SQUARE_MILE)), |
| Locale::getEnglish(), |
| u"87,650 lb/mi²", |
| u"8,765 lb/mi²", |
| u"876.5 lb/mi²", |
| u"87.65 lb/mi²", |
| u"8.765 lb/mi²", |
| u"0.8765 lb/mi²", |
| u"0.08765 lb/mi²", |
| u"0.008765 lb/mi²", |
| u"0 lb/mi²"); |
| |
| assertFormatDescending( |
| u"Joules Per Furlong Short (unit with no simplifications or special patterns)", |
| u"measure-unit/energy-joule per-measure-unit/length-furlong", |
| u"unit/joule-per-furlong", |
| NumberFormatter::with().unit(JOULE).perUnit(FURLONG), |
| Locale::getEnglish(), |
| u"87,650 J/fur", |
| u"8,765 J/fur", |
| u"876.5 J/fur", |
| u"87.65 J/fur", |
| u"8.765 J/fur", |
| u"0.8765 J/fur", |
| u"0.08765 J/fur", |
| u"0.008765 J/fur", |
| u"0 J/fur"); |
| |
| assertFormatDescending( |
| u"Joules Per Furlong Short with unit identifier via API", |
| u"measure-unit/energy-joule per-measure-unit/length-furlong", |
| u"unit/joule-per-furlong", |
| NumberFormatter::with().unit(MeasureUnit::forIdentifier("joule-per-furlong", status)), |
| Locale::getEnglish(), |
| u"87,650 J/fur", |
| u"8,765 J/fur", |
| u"876.5 J/fur", |
| u"87.65 J/fur", |
| u"8.765 J/fur", |
| u"0.8765 J/fur", |
| u"0.08765 J/fur", |
| u"0.008765 J/fur", |
| u"0 J/fur"); |
| |
| assertFormatDescending( |
| u"Pounds per Square Inch: composed", |
| u"measure-unit/force-pound-force per-measure-unit/area-square-inch", |
| u"unit/pound-force-per-square-inch", |
| NumberFormatter::with().unit(POUND_FORCE).perUnit(SQUARE_INCH), |
| Locale::getEnglish(), |
| u"87,650 psi", |
| u"8,765 psi", |
| u"876.5 psi", |
| u"87.65 psi", |
| u"8.765 psi", |
| u"0.8765 psi", |
| u"0.08765 psi", |
| u"0.008765 psi", |
| u"0 psi"); |
| |
| assertFormatDescending( |
| u"Pounds per Square Inch: built-in", |
| u"measure-unit/force-pound-force per-measure-unit/area-square-inch", |
| u"unit/pound-force-per-square-inch", |
| NumberFormatter::with().unit(MeasureUnit::getPoundPerSquareInch()), |
| Locale::getEnglish(), |
| u"87,650 psi", |
| u"8,765 psi", |
| u"876.5 psi", |
| u"87.65 psi", |
| u"8.765 psi", |
| u"0.8765 psi", |
| u"0.08765 psi", |
| u"0.008765 psi", |
| u"0 psi"); |
| |
| assertFormatSingle( |
| u"m/s/s simplifies to m/s^2", |
| u"measure-unit/speed-meter-per-second per-measure-unit/duration-second", |
| u"unit/meter-per-square-second", |
| NumberFormatter::with().unit(METER_PER_SECOND).perUnit(SECOND), |
| Locale("en-GB"), |
| 2.4, |
| u"2.4 m/s\u00B2"); |
| |
| assertFormatSingle( |
| u"Negative numbers: acceleration", |
| u"measure-unit/acceleration-meter-per-square-second", |
| u"unit/meter-per-second-second", |
| NumberFormatter::with().unit(MeasureUnit::forIdentifier("meter-per-pow2-second", status)), |
| Locale("af-ZA"), |
| -9.81, |
| u"-9,81 m/s\u00B2"); |
| |
| // Testing the rejection of invalid specifications |
| |
| // If .unit() is not given a built-in type, .perUnit() is not allowed |
| // (because .unit is now flexible enough to handle compound units, |
| // .perUnit() is supported for backward compatibility). |
| LocalizedNumberFormatter nf = NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("furlong-pascal", status)) |
| .perUnit(METER) |
| .locale("en-GB"); |
| status.assertSuccess(); // Error is only returned once we try to format. |
| FormattedNumber num = nf.formatDouble(2.4, status); |
| if (!status.expectErrorAndReset(U_UNSUPPORTED_ERROR)) { |
| errln(UnicodeString("Expected failure for unit/furlong-pascal per-unit/length-meter, got: \"") + |
| nf.formatDouble(2.4, status).toString(status) + "\"."); |
| status.assertSuccess(); |
| } |
| |
| // .perUnit() may only be passed a built-in type, or something that combines |
| // to a built-in type together with .unit(). |
| MeasureUnit SQUARE_SECOND = MeasureUnit::forIdentifier("square-second", status); |
| nf = NumberFormatter::with().unit(FURLONG).perUnit(SQUARE_SECOND).locale("en-GB"); |
| status.assertSuccess(); // Error is only returned once we try to format. |
| num = nf.formatDouble(2.4, status); |
| if (!status.expectErrorAndReset(U_UNSUPPORTED_ERROR)) { |
| errln(UnicodeString("Expected failure, got: \"") + |
| nf.formatDouble(2.4, status).toString(status) + "\"."); |
| status.assertSuccess(); |
| } |
| // As above, "square-second" is not a built-in type, however this time, |
| // meter-per-square-second is a built-in type. |
| assertFormatSingle( |
| u"meter per square-second works as a composed unit", |
| u"measure-unit/speed-meter-per-second per-measure-unit/duration-second", |
| u"unit/meter-per-square-second", |
| NumberFormatter::with().unit(METER).perUnit(SQUARE_SECOND), |
| Locale("en-GB"), |
| 2.4, |
| u"2.4 m/s\u00B2"); |
| } |
| |
| void NumberFormatterApiTest::unitArbitraryMeasureUnits() { |
| IcuTestErrorCode status(*this, "unitArbitraryMeasureUnits()"); |
| |
| // TODO: fix after data bug is resolved? See CLDR-14510. |
| // assertFormatSingle( |
| // u"Binary unit prefix: kibibyte", |
| // u"unit/kibibyte", |
| // u"unit/kibibyte", |
| // NumberFormatter::with().unit(MeasureUnit::forIdentifier("kibibyte", status)), |
| // Locale("en-GB"), |
| // 2.4, |
| // u"2.4 KiB"); |
| |
| assertFormatSingle( |
| u"Binary unit prefix: kibibyte full-name", |
| u"unit/kibibyte unit-width-full-name", |
| u"unit/kibibyte unit-width-full-name", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("kibibyte", status)) |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("en-GB"), |
| 2.4, |
| u"2.4 kibibytes"); |
| |
| assertFormatSingle( |
| u"Binary unit prefix: kibibyte full-name", |
| u"unit/kibibyte unit-width-full-name", |
| u"unit/kibibyte unit-width-full-name", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("kibibyte", status)) |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("de"), |
| 2.4, |
| u"2,4 Kibibyte"); |
| |
| assertFormatSingle( |
| u"Binary prefix for non-digital units: kibimeter", |
| u"unit/kibimeter", |
| u"unit/kibimeter", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("kibimeter", status)), |
| Locale("en-GB"), |
| 2.4, |
| u"2.4 Kim"); |
| |
| assertFormatSingle( |
| u"SI prefix falling back to root: microohm", |
| u"unit/microohm", |
| u"unit/microohm", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("microohm", status)), |
| Locale("de-CH"), |
| 2.4, |
| u"2.4 μΩ"); |
| |
| assertFormatSingle( |
| u"de-CH fallback to de: microohm unit-width-full-name", |
| u"unit/microohm unit-width-full-name", |
| u"unit/microohm unit-width-full-name", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("microohm", status)) |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("de-CH"), |
| 2.4, |
| u"2.4\u00A0Mikroohm"); |
| |
| assertFormatSingle( |
| u"No prefixes, 'times' pattern: joule-furlong", |
| u"unit/joule-furlong", |
| u"unit/joule-furlong", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("joule-furlong", status)), |
| Locale("en"), |
| 2.4, |
| u"2.4 J⋅fur"); |
| |
| assertFormatSingle( |
| u"No numeratorUnitString: per-second", |
| u"unit/per-second", |
| u"unit/per-second", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("per-second", status)), |
| Locale("de-CH"), |
| 2.4, |
| u"2.4/s"); |
| |
| assertFormatSingle( |
| u"No numeratorUnitString: per-second unit-width-full-name", |
| u"unit/per-second unit-width-full-name", |
| u"unit/per-second unit-width-full-name", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("per-second", status)) |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("de-CH"), |
| 2.4, |
| u"2.4 pro Sekunde"); |
| |
| assertFormatSingle( |
| u"Prefix in the denominator: nanogram-per-picobarrel", |
| u"unit/nanogram-per-picobarrel", |
| u"unit/nanogram-per-picobarrel", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("nanogram-per-picobarrel", status)), |
| Locale("en-ZA"), |
| 2.4, |
| u"2,4 ng/pbbl"); |
| |
| assertFormatSingle( |
| u"Prefix in the denominator: nanogram-per-picobarrel unit-width-full-name", |
| u"unit/nanogram-per-picobarrel unit-width-full-name", |
| u"unit/nanogram-per-picobarrel unit-width-full-name", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("nanogram-per-picobarrel", status)) |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("en-ZA"), |
| 2.4, |
| u"2,4 nanograms per picobarrel"); |
| |
| // Valid MeasureUnit, but unformattable, because we only have patterns for |
| // pow2 and pow3 at this time: |
| LocalizedNumberFormatter lnf = NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("pow4-mile", status)) |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME) |
| .locale("en-ZA"); |
| lnf.formatInt(1, status); |
| status.expectErrorAndReset(U_RESOURCE_TYPE_MISMATCH); |
| |
| assertFormatSingle( |
| u"kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name", |
| u"unit/kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name", |
| u"unit/kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("kibijoule-foot-per-cubic-gigafurlong-square-second", |
| status)) |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("en-ZA"), |
| 2.4, |
| u"2,4 kibijoule-feet per cubic gigafurlong-square second"); |
| |
| assertFormatSingle( |
| u"kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name", |
| u"unit/kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name", |
| u"unit/kibijoule-foot-per-cubic-gigafurlong-square-second unit-width-full-name", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("kibijoule-foot-per-cubic-gigafurlong-square-second", |
| status)) |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("de-CH"), |
| 2.4, |
| u"2.4\u00A0Kibijoule⋅Fuss pro Kubikgigafurlong⋅Quadratsekunde"); |
| |
| // TODO(ICU-21504): We want to be able to format this, but "100-kilometer" |
| // is not yet supported when it's not part of liter-per-100-kilometer: |
| lnf = NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("kilowatt-hour-per-100-kilometer", status)) |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME) |
| .locale("en-ZA"); |
| lnf.formatInt(1, status); |
| status.expectErrorAndReset(U_UNSUPPORTED_ERROR); |
| } |
| |
| // TODO: merge these tests into numbertest_skeletons.cpp instead of here: |
| void NumberFormatterApiTest::unitSkeletons() { |
| const struct TestCase { |
| const char *msg; |
| const char16_t *inputSkeleton; |
| const char16_t *normalizedSkeleton; |
| } cases[] = { |
| {"old-form built-in compound unit", // |
| u"measure-unit/speed-meter-per-second", // |
| u"unit/meter-per-second"}, |
| |
| {"old-form compound construction, converts to built-in", // |
| u"measure-unit/length-meter per-measure-unit/duration-second", // |
| u"unit/meter-per-second"}, |
| |
| {"old-form compound construction which does not simplify to a built-in", // |
| u"measure-unit/energy-joule per-measure-unit/length-meter", // |
| u"unit/joule-per-meter"}, |
| |
| {"old-form compound-compound ugliness resolves neatly", // |
| u"measure-unit/speed-meter-per-second per-measure-unit/duration-second", // |
| u"unit/meter-per-square-second"}, |
| |
| {"short-form built-in units stick with the built-in", // |
| u"unit/meter-per-second", // |
| u"unit/meter-per-second"}, |
| |
| {"short-form compound units stay as is", // |
| u"unit/square-meter-per-square-meter", // |
| u"unit/square-meter-per-square-meter"}, |
| |
| {"short-form compound units stay as is", // |
| u"unit/joule-per-furlong", // |
| u"unit/joule-per-furlong"}, |
| |
| {"short-form that doesn't consist of built-in units", // |
| u"unit/hectometer-per-second", // |
| u"unit/hectometer-per-second"}, |
| |
| {"short-form that doesn't consist of built-in units", // |
| u"unit/meter-per-hectosecond", // |
| u"unit/meter-per-hectosecond"}, |
| |
| {"percent compound skeletons handled correctly", // |
| u"unit/percent-per-meter", // |
| u"unit/percent-per-meter"}, |
| |
| {"permille compound skeletons handled correctly", // |
| u"measure-unit/concentr-permille per-measure-unit/length-meter", // |
| u"unit/permille-per-meter"}, |
| |
| {"percent simple unit is not actually considered a unit", // |
| u"unit/percent", // |
| u"percent"}, |
| |
| {"permille simple unit is not actually considered a unit", // |
| u"measure-unit/concentr-permille", // |
| u"permille"}, |
| |
| {"Round-trip example from icu-units#35", // |
| u"unit/kibijoule-per-furlong", // |
| u"unit/kibijoule-per-furlong"}, |
| }; |
| for (auto &cas : cases) { |
| IcuTestErrorCode status(*this, cas.msg); |
| auto nf = NumberFormatter::forSkeleton(cas.inputSkeleton, status); |
| if (status.errIfFailureAndReset("NumberFormatter::forSkeleton failed")) { |
| continue; |
| } |
| assertEquals( // |
| UnicodeString(TRUE, cas.inputSkeleton, -1) + u" normalization", // |
| cas.normalizedSkeleton, // |
| nf.toSkeleton(status)); |
| status.errIfFailureAndReset("NumberFormatter::toSkeleton failed"); |
| } |
| |
| const struct FailCase { |
| const char *msg; |
| const char16_t *inputSkeleton; |
| UErrorCode expectedForSkelStatus; |
| UErrorCode expectedToSkelStatus; |
| } failCases[] = { |
| {"Parsing measure-unit/* results in failure if not built-in unit", |
| u"measure-unit/hectometer", // |
| U_NUMBER_SKELETON_SYNTAX_ERROR, // |
| U_ZERO_ERROR}, |
| |
| {"Parsing per-measure-unit/* results in failure if not built-in unit", |
| u"measure-unit/meter per-measure-unit/hectosecond", // |
| U_NUMBER_SKELETON_SYNTAX_ERROR, // |
| U_ZERO_ERROR}, |
| |
| {"\"currency/EUR measure-unit/length-meter\" fails, conflicting skeleton.", |
| u"currency/EUR measure-unit/length-meter", // |
| U_NUMBER_SKELETON_SYNTAX_ERROR, // |
| U_ZERO_ERROR}, |
| |
| {"\"measure-unit/length-meter currency/EUR\" fails, conflicting skeleton.", |
| u"measure-unit/length-meter currency/EUR", // |
| U_NUMBER_SKELETON_SYNTAX_ERROR, // |
| U_ZERO_ERROR}, |
| |
| {"\"currency/EUR per-measure-unit/meter\" fails, conflicting skeleton.", |
| u"currency/EUR per-measure-unit/length-meter", // |
| U_NUMBER_SKELETON_SYNTAX_ERROR, // |
| U_ZERO_ERROR}, |
| }; |
| for (auto &cas : failCases) { |
| IcuTestErrorCode status(*this, cas.msg); |
| auto nf = NumberFormatter::forSkeleton(cas.inputSkeleton, status); |
| if (status.expectErrorAndReset(cas.expectedForSkelStatus, cas.msg)) { |
| continue; |
| } |
| nf.toSkeleton(status); |
| status.expectErrorAndReset(cas.expectedToSkelStatus, cas.msg); |
| } |
| |
| IcuTestErrorCode status(*this, "unitSkeletons"); |
| assertEquals( // |
| ".unit(METER_PER_SECOND) normalization", // |
| u"unit/meter-per-second", // |
| NumberFormatter::with().unit(METER_PER_SECOND).toSkeleton(status)); |
| assertEquals( // |
| ".unit(METER).perUnit(SECOND) normalization", // |
| u"unit/meter-per-second", |
| NumberFormatter::with().unit(METER).perUnit(SECOND).toSkeleton(status)); |
| assertEquals( // |
| ".unit(MeasureUnit::forIdentifier(\"hectometer\", status)) normalization", // |
| u"unit/hectometer", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("hectometer", status)) |
| .toSkeleton(status)); |
| assertEquals( // |
| ".unit(MeasureUnit::forIdentifier(\"hectometer\", status)) normalization", // |
| u"unit/meter-per-hectosecond", |
| NumberFormatter::with() |
| .unit(METER) |
| .perUnit(MeasureUnit::forIdentifier("hectosecond", status)) |
| .toSkeleton(status)); |
| |
| status.assertSuccess(); |
| assertEquals( // |
| ".unit(CURRENCY) produces a currency/CURRENCY skeleton", // |
| u"currency/GBP", // |
| NumberFormatter::with().unit(GBP).toSkeleton(status)); |
| status.assertSuccess(); |
| // .unit(CURRENCY).perUnit(ANYTHING) is not supported. |
| NumberFormatter::with().unit(GBP).perUnit(METER).toSkeleton(status); |
| status.expectErrorAndReset(U_UNSUPPORTED_ERROR); |
| } |
| |
| void NumberFormatterApiTest::unitUsage() { |
| IcuTestErrorCode status(*this, "unitUsage()"); |
| UnlocalizedNumberFormatter unloc_formatter; |
| LocalizedNumberFormatter formatter; |
| FormattedNumber formattedNum; |
| UnicodeString uTestCase; |
| |
| status.assertSuccess(); |
| formattedNum = |
| NumberFormatter::with().usage("road").locale(Locale::getEnglish()).formatInt(1, status); |
| status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR); |
| |
| unloc_formatter = NumberFormatter::with().usage("road").unit(MeasureUnit::getMeter()); |
| |
| uTestCase = u"unitUsage() en-ZA road"; |
| formatter = unloc_formatter.locale("en-ZA"); |
| formattedNum = formatter.formatDouble(321, status); |
| status.errIfFailureAndReset("unitUsage() en-ZA road formatDouble"); |
| assertTrue( |
| uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"", |
| MeasureUnit::getMeter() == formattedNum.getOutputUnit(status)); |
| assertEquals(uTestCase, "300 m", formattedNum.toString(status)); |
| { |
| static const UFieldPosition expectedFieldPositions[] = { |
| {UNUM_INTEGER_FIELD, 0, 3}, |
| {UNUM_MEASURE_UNIT_FIELD, 4, 5}}; |
| assertNumberFieldPositions( |
| (uTestCase + u" field positions").getTerminatedBuffer(), |
| formattedNum, |
| expectedFieldPositions, |
| UPRV_LENGTHOF(expectedFieldPositions)); |
| } |
| assertFormatDescendingBig( |
| uTestCase.getTerminatedBuffer(), |
| u"measure-unit/length-meter usage/road", |
| u"unit/meter usage/road", |
| unloc_formatter, |
| Locale("en-ZA"), |
| u"87\u00A0650 km", |
| u"8\u00A0765 km", |
| u"876 km", // 6.5 rounds down, 7.5 rounds up. |
| u"88 km", |
| u"8,8 km", |
| u"900 m", |
| u"90 m", |
| u"9 m", |
| u"0 m"); |
| |
| uTestCase = u"unitUsage() en-GB road"; |
| formatter = unloc_formatter.locale("en-GB"); |
| formattedNum = formatter.formatDouble(321, status); |
| status.errIfFailureAndReset("unitUsage() en-GB road, formatDouble(...)"); |
| assertTrue( |
| uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"", |
| MeasureUnit::getYard() == formattedNum.getOutputUnit(status)); |
| status.errIfFailureAndReset("unitUsage() en-GB road, getOutputUnit(...)"); |
| assertEquals(uTestCase, "350 yd", formattedNum.toString(status)); |
| status.errIfFailureAndReset("unitUsage() en-GB road, toString(...)"); |
| { |
| static const UFieldPosition expectedFieldPositions[] = { |
| {UNUM_INTEGER_FIELD, 0, 3}, |
| {UNUM_MEASURE_UNIT_FIELD, 4, 6}}; |
| assertNumberFieldPositions( |
| (uTestCase + u" field positions").getTerminatedBuffer(), |
| formattedNum, |
| expectedFieldPositions, |
| UPRV_LENGTHOF(expectedFieldPositions)); |
| } |
| assertFormatDescendingBig( |
| uTestCase.getTerminatedBuffer(), |
| u"measure-unit/length-meter usage/road", |
| u"unit/meter usage/road", |
| unloc_formatter, |
| Locale("en-GB"), |
| u"54,463 mi", |
| u"5,446 mi", |
| u"545 mi", |
| u"54 mi", |
| u"5.4 mi", |
| u"0.54 mi", |
| u"100 yd", |
| u"10 yd", |
| u"0 yd"); |
| |
| uTestCase = u"unitUsage() en-US road"; |
| formatter = unloc_formatter.locale("en-US"); |
| formattedNum = formatter.formatDouble(321, status); |
| status.errIfFailureAndReset("unitUsage() en-US road, formatDouble(...)"); |
| assertTrue( |
| uTestCase + u", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"", |
| MeasureUnit::getFoot() == formattedNum.getOutputUnit(status)); |
| status.errIfFailureAndReset("unitUsage() en-US road, getOutputUnit(...)"); |
| assertEquals(uTestCase, "1,050 ft", formattedNum.toString(status)); |
| status.errIfFailureAndReset("unitUsage() en-US road, toString(...)"); |
| { |
| static const UFieldPosition expectedFieldPositions[] = { |
| {UNUM_GROUPING_SEPARATOR_FIELD, 1, 2}, |
| {UNUM_INTEGER_FIELD, 0, 5}, |
| {UNUM_MEASURE_UNIT_FIELD, 6, 8}}; |
| assertNumberFieldPositions( |
| (uTestCase + u" field positions").getTerminatedBuffer(), |
| formattedNum, |
| expectedFieldPositions, |
| UPRV_LENGTHOF(expectedFieldPositions)); |
| } |
| assertFormatDescendingBig( |
| uTestCase.getTerminatedBuffer(), |
| u"measure-unit/length-meter usage/road", |
| u"unit/meter usage/road", |
| unloc_formatter, |
| Locale("en-US"), |
| u"54,463 mi", |
| u"5,446 mi", |
| u"545 mi", |
| u"54 mi", |
| u"5.4 mi", |
| u"0.54 mi", |
| u"300 ft", |
| u"30 ft", |
| u"0 ft"); |
| |
| unloc_formatter = NumberFormatter::with().usage("person").unit(MeasureUnit::getKilogram()); |
| uTestCase = u"unitUsage() en-GB person"; |
| formatter = unloc_formatter.locale("en-GB"); |
| formattedNum = formatter.formatDouble(80, status); |
| status.errIfFailureAndReset("unitUsage() en-GB person formatDouble"); |
| assertTrue( |
| uTestCase + ", got outputUnit: \"" + formattedNum.getOutputUnit(status).getIdentifier() + "\"", |
| MeasureUnit::forIdentifier("stone-and-pound", status) == formattedNum.getOutputUnit(status)); |
| status.errIfFailureAndReset("unitUsage() en-GB person - formattedNum.getOutputUnit(status)"); |
| assertEquals(uTestCase, "12 st, 8.4 lb", formattedNum.toString(status)); |
| status.errIfFailureAndReset("unitUsage() en-GB person, toString(...)"); |
| { |
| static const UFieldPosition expectedFieldPositions[] = { |
| // // Desired output: TODO(icu-units#67) |
| // {UNUM_INTEGER_FIELD, 0, 2}, |
| // {UNUM_MEASURE_UNIT_FIELD, 3, 5}, |
| // {ULISTFMT_LITERAL_FIELD, 5, 6}, |
| // {UNUM_INTEGER_FIELD, 7, 8}, |
| // {UNUM_DECIMAL_SEPARATOR_FIELD, 8, 9}, |
| // {UNUM_FRACTION_FIELD, 9, 10}, |
| // {UNUM_MEASURE_UNIT_FIELD, 11, 13}}; |
| |
| // Current output: rather no fields than wrong fields |
| {UNUM_INTEGER_FIELD, 7, 8}, |
| {UNUM_DECIMAL_SEPARATOR_FIELD, 8, 9}, |
| {UNUM_FRACTION_FIELD, 9, 10}, |
| }; |
| assertNumberFieldPositions( |
| (uTestCase + u" field positions").getTerminatedBuffer(), |
| formattedNum, |
| expectedFieldPositions, |
| UPRV_LENGTHOF(expectedFieldPositions)); |
| } |
| assertFormatDescending( |
| uTestCase.getTerminatedBuffer(), |
| u"measure-unit/mass-kilogram usage/person", |
| u"unit/kilogram usage/person", |
| unloc_formatter, |
| Locale("en-GB"), |
| u"13,802 st, 7.2 lb", |
| u"1,380 st, 3.5 lb", |
| u"138 st, 0.35 lb", |
| u"13 st, 11 lb", |
| u"1 st, 5.3 lb", |
| u"1 lb, 15 oz", |
| u"0 lb, 3.1 oz", |
| u"0 lb, 0.31 oz", |
| u"0 lb, 0 oz"); |
| |
| assertFormatDescending( |
| uTestCase.getTerminatedBuffer(), |
| u"usage/person unit-width-narrow measure-unit/mass-kilogram", |
| u"usage/person unit-width-narrow unit/kilogram", |
| unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_NARROW), |
| Locale("en-GB"), |
| u"13,802st 7.2lb", |
| u"1,380st 3.5lb", |
| u"138st 0.35lb", |
| u"13st 11lb", |
| u"1st 5.3lb", |
| u"1lb 15oz", |
| u"0lb 3.1oz", |
| u"0lb 0.31oz", |
| u"0lb 0oz"); |
| |
| assertFormatDescending( |
| uTestCase.getTerminatedBuffer(), |
| u"usage/person unit-width-short measure-unit/mass-kilogram", |
| u"usage/person unit-width-short unit/kilogram", |
| unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_SHORT), |
| Locale("en-GB"), |
| u"13,802 st, 7.2 lb", |
| u"1,380 st, 3.5 lb", |
| u"138 st, 0.35 lb", |
| u"13 st, 11 lb", |
| u"1 st, 5.3 lb", |
| u"1 lb, 15 oz", |
| u"0 lb, 3.1 oz", |
| u"0 lb, 0.31 oz", |
| u"0 lb, 0 oz"); |
| |
| assertFormatDescending( |
| uTestCase.getTerminatedBuffer(), |
| u"usage/person unit-width-full-name measure-unit/mass-kilogram", |
| u"usage/person unit-width-full-name unit/kilogram", |
| unloc_formatter.unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("en-GB"), |
| u"13,802 stone, 7.2 pounds", |
| u"1,380 stone, 3.5 pounds", |
| u"138 stone, 0.35 pounds", |
| u"13 stone, 11 pounds", |
| u"1 stone, 5.3 pounds", |
| u"1 pound, 15 ounces", |
| u"0 pounds, 3.1 ounces", |
| u"0 pounds, 0.31 ounces", |
| u"0 pounds, 0 ounces"); |
| |
| assertFormatDescendingBig( |
| u"Scientific notation with Usage: possible when using a reasonable Precision", |
| u"scientific @### usage/default measure-unit/area-square-meter unit-width-full-name", |
| u"scientific @### usage/default unit/square-meter unit-width-full-name", |
| NumberFormatter::with() |
| .unit(SQUARE_METER) |
| .usage("default") |
| .notation(Notation::scientific()) |
| .precision(Precision::minMaxSignificantDigits(1, 4)) |
| .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("en-ZA"), |
| u"8,765E1 square kilometres", |
| u"8,765E0 square kilometres", |
| u"8,765E1 hectares", |
| u"8,765E0 hectares", |
| u"8,765E3 square metres", |
| u"8,765E2 square metres", |
| u"8,765E1 square metres", |
| u"8,765E0 square metres", |
| u"0E0 square centimetres"); |
| |
| assertFormatSingle( |
| u"Negative Infinity with Unit Preferences", |
| u"measure-unit/area-acre usage/default", |
| u"unit/acre usage/default", |
| NumberFormatter::with().unit(MeasureUnit::getAcre()).usage("default"), |
| Locale::getEnglish(), |
| -uprv_getInfinity(), |
| u"-∞ km²"); |
| |
| // // TODO(icu-units#131): do we care about NaN? |
| // // TODO: on some platforms with MSVC, "-NaN sec" is returned. |
| // assertFormatSingle( |
| // u"NaN with Unit Preferences", |
| // u"measure-unit/area-acre usage/default", |
| // u"unit/acre usage/default", |
| // NumberFormatter::with().unit(MeasureUnit::getAcre()).usage("default"), |
| // Locale::getEnglish(), |
| // uprv_getNaN(), |
| // u"NaN cm²"); |
| |
| assertFormatSingle( |
| u"Negative numbers: minute-and-second", |
| u"measure-unit/duration-second usage/media", |
| u"unit/second usage/media", |
| NumberFormatter::with().unit(SECOND).usage("media"), |
| Locale("nl-NL"), |
| -77.7, |
| u"-1 min, 18 sec"); |
| |
| assertFormatSingle( |
| u"Negative numbers: media seconds", |
| u"measure-unit/duration-second usage/media", |
| u"unit/second usage/media", |
| NumberFormatter::with().unit(SECOND).usage("media"), |
| Locale("nl-NL"), |
| -2.7, |
| u"-2,7 sec"); |
| |
| // // TODO: on some platforms with MSVC, "-NaN sec" is returned. |
| // assertFormatSingle( |
| // u"NaN minute-and-second", |
| // u"measure-unit/duration-second usage/media", |
| // u"unit/second usage/media", |
| // NumberFormatter::with().unit(SECOND).usage("media"), |
| // Locale("nl-NL"), |
| // uprv_getNaN(), |
| // u"NaN sec"); |
| |
| assertFormatSingle( |
| u"NaN meter-and-centimeter", |
| u"measure-unit/length-meter usage/person-height", |
| u"unit/meter usage/person-height", |
| NumberFormatter::with().unit(METER).usage("person-height"), |
| Locale("de-DE"), |
| uprv_getNaN(), |
| u"0 m, NaN cm"); |
| |
| assertFormatSingle( |
| u"Rounding Mode propagates: rounding down", |
| u"usage/road measure-unit/length-centimeter rounding-mode-floor", |
| u"usage/road unit/centimeter rounding-mode-floor", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("centimeter", status)) |
| .usage("road") |
| .roundingMode(UNUM_ROUND_FLOOR), |
| Locale("en-ZA"), |
| 34500, |
| u"300 m"); |
| |
| assertFormatSingle( |
| u"Rounding Mode propagates: rounding up", |
| u"usage/road measure-unit/length-centimeter rounding-mode-ceiling", |
| u"usage/road unit/centimeter rounding-mode-ceiling", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("centimeter", status)) |
| .usage("road") |
| .roundingMode(UNUM_ROUND_CEILING), |
| Locale("en-ZA"), |
| 30500, |
| u"350 m"); |
| |
| // TODO(icu-units#38): improve unit testing coverage. E.g. add vehicle-fuel |
| // triggering inversion conversion code. Test with 0 too, to see |
| // divide-by-zero behaviour. |
| } |
| |
| void NumberFormatterApiTest::unitUsageErrorCodes() { |
| IcuTestErrorCode status(*this, "unitUsageErrorCodes()"); |
| UnlocalizedNumberFormatter unloc_formatter; |
| |
| unloc_formatter = NumberFormatter::forSkeleton(u"unit/foobar", status); |
| // This gives an error, because foobar is an invalid unit: |
| status.expectErrorAndReset(U_NUMBER_SKELETON_SYNTAX_ERROR); |
| |
| unloc_formatter = NumberFormatter::forSkeleton(u"usage/foobar", status); |
| // This does not give an error, because usage is not looked up yet. |
| status.errIfFailureAndReset("Expected behaviour: no immediate error for invalid usage"); |
| unloc_formatter.locale("en-GB").formatInt(1, status); |
| // Lacking a unit results in a failure. The skeleton is "incomplete", but we |
| // support adding the unit via the fluent API, so it is not an error until |
| // we build the formatting pipeline itself. |
| status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR); |
| // Adding the unit as part of the fluent chain leads to success. |
| unloc_formatter.unit(MeasureUnit::getMeter()).locale("en-GB").formatInt(1, status); |
| status.assertSuccess(); |
| |
| // Setting unit to the "base dimensionless unit" is like clearing unit. |
| unloc_formatter = NumberFormatter::with().unit(MeasureUnit()).usage("default"); |
| // This does not give an error, because usage-vs-unit isn't resolved yet. |
| status.errIfFailureAndReset("Expected behaviour: no immediate error for invalid unit"); |
| unloc_formatter.locale("en-GB").formatInt(1, status); |
| status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR); |
| } |
| |
| // Tests for the "skeletons" field in unitPreferenceData, as well as precision |
| // and notation overrides. |
| void NumberFormatterApiTest::unitUsageSkeletons() { |
| IcuTestErrorCode status(*this, "unitUsageSkeletons()"); |
| |
| assertFormatSingle( |
| u"Default >300m road preference skeletons round to 50m", |
| u"usage/road measure-unit/length-meter", |
| u"usage/road unit/meter", |
| NumberFormatter::with().unit(METER).usage("road"), |
| Locale("en-ZA"), |
| 321, |
| u"300 m"); |
| |
| assertFormatSingle( |
| u"Precision can be overridden: override takes precedence", |
| u"usage/road measure-unit/length-meter @#", |
| u"usage/road unit/meter @#", |
| NumberFormatter::with() |
| .unit(METER) |
| .usage("road") |
| .precision(Precision::maxSignificantDigits(2)), |
| Locale("en-ZA"), |
| 321, |
| u"320 m"); |
| |
| assertFormatSingle( |
| u"Compact notation with Usage: bizarre, but possible (short)", |
| u"compact-short usage/road measure-unit/length-meter", |
| u"compact-short usage/road unit/meter", |
| NumberFormatter::with() |
| .unit(METER) |
| .usage("road") |
| .notation(Notation::compactShort()), |
| Locale("en-ZA"), |
| 987654321, |
| u"988K km"); |
| |
| assertFormatSingle( |
| u"Compact notation with Usage: bizarre, but possible (short, precision override)", |
| u"compact-short usage/road measure-unit/length-meter @#", |
| u"compact-short usage/road unit/meter @#", |
| NumberFormatter::with() |
| .unit(METER) |
| .usage("road") |
| .notation(Notation::compactShort()) |
| .precision(Precision::maxSignificantDigits(2)), |
| Locale("en-ZA"), |
| 987654321, |
| u"990K km"); |
| |
| assertFormatSingle( |
| u"Compact notation with Usage: unusual but possible (long)", |
| u"compact-long usage/road measure-unit/length-meter @#", |
| u"compact-long usage/road unit/meter @#", |
| NumberFormatter::with() |
| .unit(METER) |
| .usage("road") |
| .notation(Notation::compactLong()) |
| .precision(Precision::maxSignificantDigits(2)), |
| Locale("en-ZA"), |
| 987654321, |
| u"990 thousand km"); |
| |
| assertFormatSingle( |
| u"Compact notation with Usage: unusual but possible (long, precision override)", |
| u"compact-long usage/road measure-unit/length-meter @#", |
| u"compact-long usage/road unit/meter @#", |
| NumberFormatter::with() |
| .unit(METER) |
| .usage("road") |
| .notation(Notation::compactLong()) |
| .precision(Precision::maxSignificantDigits(2)), |
| Locale("en-ZA"), |
| 987654321, |
| u"990 thousand km"); |
| |
| assertFormatSingle( |
| u"Scientific notation, not recommended, requires precision override for road", |
| u"scientific usage/road measure-unit/length-meter", |
| u"scientific usage/road unit/meter", |
| NumberFormatter::with().unit(METER).usage("road").notation(Notation::scientific()), |
| Locale("en-ZA"), |
| 321.45, |
| // Rounding to the nearest "50" is not exponent-adjusted in scientific notation: |
| u"0E2 m"); |
| |
| assertFormatSingle( |
| u"Scientific notation with Usage: possible when using a reasonable Precision", |
| u"scientific usage/road measure-unit/length-meter @###", |
| u"scientific usage/road unit/meter @###", |
| NumberFormatter::with() |
| .unit(METER) |
| .usage("road") |
| .notation(Notation::scientific()) |
| .precision(Precision::maxSignificantDigits(4)), |
| Locale("en-ZA"), |
| 321.45, // 0.45 rounds down, 0.55 rounds up. |
| u"3,214E2 m"); |
| |
| assertFormatSingle( |
| u"Scientific notation with Usage: possible when using a reasonable Precision", |
| u"scientific usage/default measure-unit/length-astronomical-unit unit-width-full-name", |
| u"scientific usage/default unit/astronomical-unit unit-width-full-name", |
| NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier("astronomical-unit", status)) |
| .usage("default") |
| .notation(Notation::scientific()) |
| .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("en-ZA"), |
| 1e20, |
| u"1,5E28 kilometres"); |
| |
| status.assertSuccess(); |
| } |
| |
| void NumberFormatterApiTest::unitCurrency() { |
| assertFormatDescending( |
| u"Currency", |
| u"currency/GBP", |
| u"currency/GBP", |
| NumberFormatter::with().unit(GBP), |
| Locale::getEnglish(), |
| u"£87,650.00", |
| u"£8,765.00", |
| u"£876.50", |
| u"£87.65", |
| u"£8.76", |
| u"£0.88", |
| u"£0.09", |
| u"£0.01", |
| u"£0.00"); |
| |
| assertFormatDescending( |
| u"Currency ISO", |
| u"currency/GBP unit-width-iso-code", |
| u"currency/GBP unit-width-iso-code", |
| NumberFormatter::with().unit(GBP).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE), |
| Locale::getEnglish(), |
| u"GBP 87,650.00", |
| u"GBP 8,765.00", |
| u"GBP 876.50", |
| u"GBP 87.65", |
| u"GBP 8.76", |
| u"GBP 0.88", |
| u"GBP 0.09", |
| u"GBP 0.01", |
| u"GBP 0.00"); |
| |
| assertFormatDescending( |
| u"Currency Long Name", |
| u"currency/GBP unit-width-full-name", |
| u"currency/GBP unit-width-full-name", |
| NumberFormatter::with().unit(GBP).unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale::getEnglish(), |
| u"87,650.00 British pounds", |
| u"8,765.00 British pounds", |
| u"876.50 British pounds", |
| u"87.65 British pounds", |
| u"8.76 British pounds", |
| u"0.88 British pounds", |
| u"0.09 British pounds", |
| u"0.01 British pounds", |
| u"0.00 British pounds"); |
| |
| assertFormatDescending( |
| u"Currency Hidden", |
| u"currency/GBP unit-width-hidden", |
| u"currency/GBP unit-width-hidden", |
| NumberFormatter::with().unit(GBP).unitWidth(UNUM_UNIT_WIDTH_HIDDEN), |
| Locale::getEnglish(), |
| u"87,650.00", |
| u"8,765.00", |
| u"876.50", |
| u"87.65", |
| u"8.76", |
| u"0.88", |
| u"0.09", |
| u"0.01", |
| u"0.00"); |
| |
| // TODO: Implement Measure in C++ |
| // assertFormatSingleMeasure( |
| // u"Currency with CurrencyAmount Input", |
| // NumberFormatter::with(), |
| // Locale::getEnglish(), |
| // new CurrencyAmount(5.43, GBP), |
| // u"£5.43"); |
| |
| // TODO: Enable this test when DecimalFormat wrapper is done. |
| // assertFormatSingle( |
| // u"Currency Long Name from Pattern Syntax", NumberFormatter.fromDecimalFormat( |
| // PatternStringParser.parseToProperties("0 ¤¤¤"), |
| // DecimalFormatSymbols.getInstance(Locale::getEnglish()), |
| // null).unit(GBP), Locale::getEnglish(), 1234567.89, u"1234568 British pounds"); |
| |
| assertFormatSingle( |
| u"Currency with Negative Sign", |
| u"currency/GBP", |
| u"currency/GBP", |
| NumberFormatter::with().unit(GBP), |
| Locale::getEnglish(), |
| -9876543.21, |
| u"-£9,876,543.21"); |
| |
| // The full currency symbol is not shown in NARROW format. |
| // NOTE: This example is in the documentation. |
| assertFormatSingle( |
| u"Currency Difference between Narrow and Short (Narrow Version)", |
| u"currency/USD unit-width-narrow", |
| u"currency/USD unit-width-narrow", |
| NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_NARROW), |
| Locale("en-CA"), |
| 5.43, |
| u"$5.43"); |
| |
| assertFormatSingle( |
| u"Currency Difference between Narrow and Short (Short Version)", |
| u"currency/USD unit-width-short", |
| u"currency/USD unit-width-short", |
| NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT), |
| Locale("en-CA"), |
| 5.43, |
| u"US$5.43"); |
| |
| assertFormatSingle( |
| u"Currency Difference between Formal and Short (Formal Version)", |
| u"currency/TWD unit-width-formal", |
| u"currency/TWD unit-width-formal", |
| NumberFormatter::with().unit(TWD).unitWidth(UNUM_UNIT_WIDTH_FORMAL), |
| Locale("zh-TW"), |
| 5.43, |
| u"NT$5.43"); |
| |
| assertFormatSingle( |
| u"Currency Difference between Formal and Short (Short Version)", |
| u"currency/TWD unit-width-short", |
| u"currency/TWD unit-width-short", |
| NumberFormatter::with().unit(TWD).unitWidth(UNUM_UNIT_WIDTH_SHORT), |
| Locale("zh-TW"), |
| 5.43, |
| u"$5.43"); |
| |
| assertFormatSingle( |
| u"Currency Difference between Variant and Short (Formal Version)", |
| u"currency/TRY unit-width-variant", |
| u"currency/TRY unit-width-variant", |
| NumberFormatter::with().unit(TRY).unitWidth(UNUM_UNIT_WIDTH_VARIANT), |
| Locale("tr-TR"), |
| 5.43, |
| u"TL\u00A05,43"); |
| |
| assertFormatSingle( |
| u"Currency Difference between Variant and Short (Short Version)", |
| u"currency/TRY unit-width-short", |
| u"currency/TRY unit-width-short", |
| NumberFormatter::with().unit(TRY).unitWidth(UNUM_UNIT_WIDTH_SHORT), |
| Locale("tr-TR"), |
| 5.43, |
| u"₺5,43"); |
| |
| assertFormatSingle( |
| u"Currency-dependent format (Control)", |
| u"currency/USD unit-width-short", |
| u"currency/USD unit-width-short", |
| NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT), |
| Locale("ca"), |
| 444444.55, |
| u"444.444,55 USD"); |
| |
| assertFormatSingle( |
| u"Currency-dependent format (Test)", |
| u"currency/ESP unit-width-short", |
| u"currency/ESP unit-width-short", |
| NumberFormatter::with().unit(ESP).unitWidth(UNUM_UNIT_WIDTH_SHORT), |
| Locale("ca"), |
| 444444.55, |
| u"₧ 444.445"); |
| |
| assertFormatSingle( |
| u"Currency-dependent symbols (Control)", |
| u"currency/USD unit-width-short", |
| u"currency/USD unit-width-short", |
| NumberFormatter::with().unit(USD).unitWidth(UNUM_UNIT_WIDTH_SHORT), |
| Locale("pt-PT"), |
| 444444.55, |
| u"444 444,55 US$"); |
| |
| // NOTE: This is a bit of a hack on CLDR's part. They set the currency symbol to U+200B (zero- |
| // width space), and they set the decimal separator to the $ symbol. |
| assertFormatSingle( |
| u"Currency-dependent symbols (Test Short)", |
| u"currency/PTE unit-width-short", |
| u"currency/PTE unit-width-short", |
| NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_SHORT), |
| Locale("pt-PT"), |
| 444444.55, |
| u"444,444$55 \u200B"); |
| |
| assertFormatSingle( |
| u"Currency-dependent symbols (Test Narrow)", |
| u"currency/PTE unit-width-narrow", |
| u"currency/PTE unit-width-narrow", |
| NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_NARROW), |
| Locale("pt-PT"), |
| 444444.55, |
| u"444,444$55 \u200B"); |
| |
| assertFormatSingle( |
| u"Currency-dependent symbols (Test ISO Code)", |
| u"currency/PTE unit-width-iso-code", |
| u"currency/PTE unit-width-iso-code", |
| NumberFormatter::with().unit(PTE).unitWidth(UNUM_UNIT_WIDTH_ISO_CODE), |
| Locale("pt-PT"), |
| 444444.55, |
| u"444,444$55 PTE"); |
| |
| assertFormatSingle( |
| u"Plural form depending on visible digits (ICU-20499)", |
| u"currency/RON unit-width-full-name", |
| u"currency/RON unit-width-full-name", |
| NumberFormatter::with().unit(RON).unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale("ro-RO"), |
| 24, |
| u"24,00 lei românești"); |
| |
| assertFormatSingle( |
| u"Currency spacing in suffix (ICU-20954)", |
| u"currency/CNY", |
| u"currency/CNY", |
| NumberFormatter::with().unit(CNY), |
| Locale("lu"), |
| 123.12, |
| u"123,12 CN¥"); |
| } |
| |
| void NumberFormatterApiTest::runUnitInflectionsTestCases(UnlocalizedNumberFormatter unf, |
| UnicodeString skeleton, |
| const UnitInflectionTestCase *cases, |
| int32_t numCases, |
| IcuTestErrorCode &status) { |
| for (int32_t i = 0; i < numCases; i++) { |
| UnitInflectionTestCase t = cases[i]; |
| status.assertSuccess(); |
| MeasureUnit mu = MeasureUnit::forIdentifier(t.unitIdentifier, status); |
| if (status.errIfFailureAndReset("MeasureUnit::forIdentifier(\"%s\", ...) failed", |
| t.unitIdentifier)) { |
| continue; |
| }; |
| UnicodeString skelString = UnicodeString("unit/") + t.unitIdentifier + u" " + skeleton; |
| const UChar *skel; |
| const UChar *cSkel; |
| if (t.unitDisplayCase == nullptr || t.unitDisplayCase[0] == 0) { |
| unf = unf.unit(mu).unitDisplayCase(""); |
| skel = skelString.getTerminatedBuffer(); |
| cSkel = skelString.getTerminatedBuffer(); |
| } else { |
| unf = unf.unit(mu).unitDisplayCase(t.unitDisplayCase); |
| // No skeleton support for unitDisplayCase yet. |
| skel = nullptr; |
| cSkel = nullptr; |
| } |
| assertFormatSingle((UnicodeString("Unit: \"") + t.unitIdentifier + ("\", \"") + skeleton + |
| u"\", locale=\"" + t.locale + u"\", case=\"" + |
| (t.unitDisplayCase ? t.unitDisplayCase : "") + u"\", value=" + t.value) |
| .getTerminatedBuffer(), |
| skel, cSkel, unf, Locale(t.locale), t.value, t.expected); |
| status.assertSuccess(); |
| } |
| } |
| |
| void NumberFormatterApiTest::unitInflections() { |
| IcuTestErrorCode status(*this, "unitInflections"); |
| |
| UnlocalizedNumberFormatter unf; |
| const UChar *skeleton; |
| { |
| // Simple inflected form test - test case based on the example in CLDR's |
| // grammaticalFeatures.xml |
| unf = NumberFormatter::with().unitWidth(UNUM_UNIT_WIDTH_FULL_NAME); |
| skeleton = u"unit-width-full-name"; |
| const UnitInflectionTestCase percentCases[] = { |
| {"percent", "ru", nullptr, 10, u"10 процентов"}, // many |
| {"percent", "ru", "genitive", 10, u"10 процентов"}, // many |
| {"percent", "ru", nullptr, 33, u"33 процента"}, // few |
| {"percent", "ru", "genitive", 33, u"33 процентов"}, // few |
| {"percent", "ru", nullptr, 1, u"1 процент"}, // one |
| {"percent", "ru", "genitive", 1, u"1 процента"}, // one |
| }; |
| runUnitInflectionsTestCases(unf, skeleton, percentCases, UPRV_LENGTHOF(percentCases), status); |
| } |
| { |
| // General testing of inflection rules |
| unf = NumberFormatter::with().unitWidth(UNUM_UNIT_WIDTH_FULL_NAME); |
| skeleton = u"unit-width-full-name"; |
| const UnitInflectionTestCase meterCases[] = { |
| // Check up on the basic values that the compound patterns below are |
| // derived from: |
| {"meter", "de", nullptr, 1, u"1 Meter"}, |
| {"meter", "de", "genitive", 1, u"1 Meters"}, |
| {"meter", "de", nullptr, 2, u"2 Meter"}, |
| {"meter", "de", "dative", 2, u"2 Metern"}, |
| {"mile", "de", nullptr, 1, u"1 Meile"}, |
| {"mile", "de", nullptr, 2, u"2 Meilen"}, |
| {"day", "de", nullptr, 1, u"1 Tag"}, |
| {"day", "de", "genitive", 1, u"1 Tages"}, |
| {"day", "de", nullptr, 2, u"2 Tage"}, |
| {"day", "de", "dative", 2, u"2 Tagen"}, |
| {"decade", "de", nullptr, 1, u"1\u00A0Jahrzehnt"}, |
| {"decade", "de", nullptr, 2, u"2\u00A0Jahrzehnte"}, |
| |
| // Testing de "per" rules: |
| // <deriveComponent feature="case" structure="per" value0="compound" value1="accusative"/> |
| // <deriveComponent feature="plural" structure="per" value0="compound" value1="one"/> |
| // per-patterns use accusative, but since the accusative form |
| // matches the nominative form, we're not effectively testing value1 |
| // in the "case & per" rule above. |
| |
| // We have a perUnitPattern for "day" in de, so "per" rules are not |
| // applied for these: |
| {"meter-per-day", "de", nullptr, 1, u"1 Meter pro Tag"}, |
| {"meter-per-day", "de", "genitive", 1, u"1 Meters pro Tag"}, |
| {"meter-per-day", "de", nullptr, 2, u"2 Meter pro Tag"}, |
| {"meter-per-day", "de", "dative", 2, u"2 Metern pro Tag"}, |
| |
| // testing code path that falls back to "root" grammaticalFeatures |
| // but does not inflect: |
| {"meter-per-day", "af", nullptr, 1, u"1 meter per dag"}, |
| {"meter-per-day", "af", "dative", 1, u"1 meter per dag"}, |
| |
| // Decade does not have a perUnitPattern at this time (CLDR 39 / ICU |
| // 69), so we can use it to test for selection of correct plural form. |
| // - Note: fragile test cases, these cases will break when |
| // whitespace is more consistently applied. |
| {"parsec-per-decade", "de", nullptr, 1, u"1\u00A0Parsec pro Jahrzehnt"}, |
| {"parsec-per-decade", "de", "genitive", 1, u"1 Parsec pro Jahrzehnt"}, |
| {"parsec-per-decade", "de", nullptr, 2, u"2\u00A0Parsec pro Jahrzehnt"}, |
| {"parsec-per-decade", "de", "dative", 2, u"2 Parsec pro Jahrzehnt"}, |
| |
| // Testing de "times", "power" and "prefix" rules: |
| // |
| // <deriveComponent feature="plural" structure="times" value0="one" value1="compound"/> |
| // <deriveComponent feature="case" structure="times" value0="nominative" value1="compound"/> |
| // |
| // <deriveComponent feature="plural" structure="prefix" value0="one" value1="compound"/> |
| // <deriveComponent feature="case" structure="prefix" value0="nominative" value1="compound"/> |
| // |
| // Prefixes in German don't change with plural or case, so these |
| // tests can't test value0 of the following two rules: |
| // <deriveComponent feature="plural" structure="power" value0="one" value1="compound"/> |
| // <deriveComponent feature="case" structure="power" value0="nominative" value1="compound"/> |
| {"square-decimeter-dekameter", "de", nullptr, 1, u"1 Quadratdezimeter⋅Dekameter"}, |
| {"square-decimeter-dekameter", "de", "genitive", 1, u"1 Quadratdezimeter⋅Dekameters"}, |
| {"square-decimeter-dekameter", "de", nullptr, 2, u"2 Quadratdezimeter⋅Dekameter"}, |
| {"square-decimeter-dekameter", "de", "dative", 2, u"2 Quadratdezimeter⋅Dekametern"}, |
| // Feminine "Meile" better demonstrates singular-vs-plural form: |
| {"cubic-mile-dekamile", "de", nullptr, 1, u"1 Kubikmeile⋅Dekameile"}, |
| {"cubic-mile-dekamile", "de", nullptr, 2, u"2 Kubikmeile⋅Dekameilen"}, |
| |
| // French handles plural "times" and "power" structures differently: |
| // plural form impacts all "numerator" units (denominator remains |
| // singular like German), and "pow2" prefixes have different forms |
| // <deriveComponent feature="plural" structure="times" value0="compound" value1="compound"/> |
| // <deriveComponent feature="plural" structure="power" value0="compound" value1="compound"/> |
| {"square-decimeter-square-second", "fr", nullptr, 1, u"1\u00A0décimètre carré-seconde carrée"}, |
| {"square-decimeter-square-second", "fr", nullptr, 2, u"2\u00A0décimètres carrés-secondes carrées"}, |
| }; |
| runUnitInflectionsTestCases(unf, skeleton, meterCases, UPRV_LENGTHOF(meterCases), status); |
| } |
| { |
| // Testing inflection of mixed units: |
| unf = NumberFormatter::with().unitWidth(UNUM_UNIT_WIDTH_FULL_NAME); |
| skeleton = u"unit-width-full-name"; |
| const UnitInflectionTestCase meterPerDayCases[] = { |
| {"meter", "de", nullptr, 1, u"1 Meter"}, |
| {"meter", "de", "genitive", 1, u"1 Meters"}, |
| {"meter", "de", "dative", 2, u"2 Metern"}, |
| {"centimeter", "de", nullptr, 1, u"1 Zentimeter"}, |
| {"centimeter", "de", "genitive", 1, u"1 Zentimeters"}, |
| {"centimeter", "de", "dative", 10, u"10 Zentimetern"}, |
| // TODO(CLDR-14502): check that these inflections are correct, and |
| // whether CLDR needs any rules for them (presumably CLDR spec |
| // should mention it, if it's a consistent rule): |
| {"meter-and-centimeter", "de", nullptr, 1.01, u"1 Meter, 1 Zentimeter"}, |
| {"meter-and-centimeter", "de", "genitive", 1.01, u"1 Meters, 1 Zentimeters"}, |
| {"meter-and-centimeter", "de", "genitive", 1.1, u"1 Meters, 10 Zentimeter"}, |
| {"meter-and-centimeter", "de", "dative", 1.1, u"1 Meter, 10 Zentimetern"}, |
| {"meter-and-centimeter", "de", "dative", 2.1, u"2 Metern, 10 Zentimetern"}, |
| }; |
| runUnitInflectionsTestCases(unf, skeleton, meterPerDayCases, UPRV_LENGTHOF(meterPerDayCases), |
| status); |
| } |
| // TODO: add a usage case that selects between preferences with different |
| // genders (e.g. year, month, day, hour). |
| // TODO: look at "↑↑↑" cases: check that inheritance is done right. |
| } |
| |
| void NumberFormatterApiTest::unitGender() { |
| IcuTestErrorCode status(*this, "unitGender"); |
| |
| const struct TestCase { |
| const char *locale; |
| const char *unitIdentifier; |
| const char *expectedGender; |
| } cases[] = { |
| {"de", "meter", "masculine"}, |
| {"de", "second", "feminine"}, |
| {"de", "minute", "feminine"}, |
| {"de", "hour", "feminine"}, |
| {"de", "day", "masculine"}, |
| {"de", "year", "neuter"}, |
| {"fr", "meter", "masculine"}, |
| {"fr", "second", "feminine"}, |
| {"fr", "minute", "feminine"}, |
| {"fr", "hour", "feminine"}, |
| {"fr", "day", "masculine"}, |
| // grammaticalFeatures deriveCompound "per" rule takes the gender of the |
| // numerator unit: |
| {"de", "meter-per-hour", "masculine"}, |
| {"fr", "meter-per-hour", "masculine"}, |
| {"af", "meter-per-hour", ""}, // ungendered language |
| // French "times" takes gender from first value, German takes the |
| // second. Prefix and power does not have impact on gender for these |
| // languages: |
| {"de", "square-decimeter-square-second", "feminine"}, |
| {"fr", "square-decimeter-square-second", "masculine"}, |
| // TODO(ICU-21494): determine whether list genders behave as follows, |
| // and implement proper getListGender support (covering more than just |
| // two genders): |
| // // gender rule for lists of people: de "neutral", fr "maleTaints" |
| // {"de", "day-and-hour-and-minute", "neuter"}, |
| // {"de", "hour-and-minute", "feminine"}, |
| // {"fr", "day-and-hour-and-minute", "masculine"}, |
| // {"fr", "hour-and-minute", "feminine"}, |
| }; |
| LocalizedNumberFormatter formatter; |
| FormattedNumber fn; |
| for (const TestCase &t : cases) { |
| // TODO(icu-units#140): make this work for more than just UNUM_UNIT_WIDTH_FULL_NAME |
| // formatter = NumberFormatter::with() |
| // .unit(MeasureUnit::forIdentifier(t.unitIdentifier, status)) |
| // .locale(Locale(t.locale)); |
| // fn = formatter.formatDouble(1.1, status); |
| // assertEquals(UnicodeString("Testing gender with default width, unit: ") + t.unitIdentifier + |
| // ", locale: " + t.locale, |
| // t.expectedGender, fn.getGender(status)); |
| // status.assertSuccess(); |
| |
| formatter = NumberFormatter::with() |
| .unit(MeasureUnit::forIdentifier(t.unitIdentifier, status)) |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME) |
| .locale(Locale(t.locale)); |
| fn = formatter.formatDouble(1.1, status); |
| assertEquals(UnicodeString("Testing gender with UNUM_UNIT_WIDTH_FULL_NAME, unit: ") + |
| t.unitIdentifier + ", locale: " + t.locale, |
| t.expectedGender, fn.getGender(status)); |
| status.assertSuccess(); |
| } |
| |
| // Make sure getGender does not return garbage for genderless languages |
| formatter = NumberFormatter::with().locale(Locale::getEnglish()); |
| fn = formatter.formatDouble(1.1, status); |
| status.assertSuccess(); |
| assertEquals("getGender for a genderless language", "", fn.getGender(status)); |
| } |
| |
| void NumberFormatterApiTest::unitPercent() { |
| assertFormatDescending( |
| u"Percent", |
| u"percent", |
| u"%", |
| NumberFormatter::with().unit(NoUnit::percent()), |
| Locale::getEnglish(), |
| u"87,650%", |
| u"8,765%", |
| u"876.5%", |
| u"87.65%", |
| u"8.765%", |
| u"0.8765%", |
| u"0.08765%", |
| u"0.008765%", |
| u"0%"); |
| |
| assertFormatDescending( |
| u"Permille", |
| u"permille", |
| u"permille", |
| NumberFormatter::with().unit(NoUnit::permille()), |
| Locale::getEnglish(), |
| u"87,650‰", |
| u"8,765‰", |
| u"876.5‰", |
| u"87.65‰", |
| u"8.765‰", |
| u"0.8765‰", |
| u"0.08765‰", |
| u"0.008765‰", |
| u"0‰"); |
| |
| assertFormatSingle( |
| u"NoUnit Base", |
| u"base-unit", |
| u"", |
| NumberFormatter::with().unit(NoUnit::base()), |
| Locale::getEnglish(), |
| 51423, |
| u"51,423"); |
| |
| assertFormatSingle( |
| u"Percent with Negative Sign", |
| u"percent", |
| u"%", |
| NumberFormatter::with().unit(NoUnit::percent()), |
| Locale::getEnglish(), |
| -98.7654321, |
| u"-98.765432%"); |
| |
| // ICU-20923 |
| assertFormatDescendingBig( |
| u"Compact Percent", |
| u"compact-short percent", |
| u"K %", |
| NumberFormatter::with() |
| .notation(Notation::compactShort()) |
| .unit(NoUnit::percent()), |
| Locale::getEnglish(), |
| u"88M%", |
| u"8.8M%", |
| u"876K%", |
| u"88K%", |
| u"8.8K%", |
| u"876%", |
| u"88%", |
| u"8.8%", |
| u"0%"); |
| |
| // ICU-20923 |
| assertFormatDescendingBig( |
| u"Compact Percent with Scale", |
| u"compact-short percent scale/100", |
| u"K %x100", |
| NumberFormatter::with() |
| .notation(Notation::compactShort()) |
| .unit(NoUnit::percent()) |
| .scale(Scale::powerOfTen(2)), |
| Locale::getEnglish(), |
| u"8.8B%", |
| u"876M%", |
| u"88M%", |
| u"8.8M%", |
| u"876K%", |
| u"88K%", |
| u"8.8K%", |
| u"876%", |
| u"0%"); |
| |
| // ICU-20923 |
| assertFormatDescendingBig( |
| u"Compact Percent Long Name", |
| u"compact-short percent unit-width-full-name", |
| u"K % unit-width-full-name", |
| NumberFormatter::with() |
| .notation(Notation::compactShort()) |
| .unit(NoUnit::percent()) |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale::getEnglish(), |
| u"88M percent", |
| u"8.8M percent", |
| u"876K percent", |
| u"88K percent", |
| u"8.8K percent", |
| u"876 percent", |
| u"88 percent", |
| u"8.8 percent", |
| u"0 percent"); |
| |
| assertFormatSingle( |
| u"Per Percent", |
| u"measure-unit/length-meter per-measure-unit/concentr-percent unit-width-full-name", |
| u"measure-unit/length-meter per-measure-unit/concentr-percent unit-width-full-name", |
| NumberFormatter::with() |
| .unit(MeasureUnit::getMeter()) |
| .perUnit(MeasureUnit::getPercent()) |
| .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME), |
| Locale::getEnglish(), |
| 50, |
| u"50 meters per percent"); |
| } |
| |
| void NumberFormatterApiTest::percentParity() { |
| IcuTestErrorCode status(*this, "percentParity"); |
| UnlocalizedNumberFormatter uNoUnitPercent = NumberFormatter::with().unit(NoUnit::percent()); |
| UnlocalizedNumberFormatter uNoUnitPermille = NumberFormatter::with().unit(NoUnit::permille()); |
| UnlocalizedNumberFormatter uMeasurePercent = NumberFormatter::with().unit(MeasureUnit::getPercent()); |
| UnlocalizedNumberFormatter uMeasurePermille = NumberFormatter::with().unit(MeasureUnit::getPermille()); |
| |
| int32_t localeCount; |
| auto locales = Locale::getAvailableLocales(localeCount); |
| for (int32_t i=0; i<localeCount; i++) { |
| auto& locale = locales[i]; |
| UnicodeString sNoUnitPercent = uNoUnitPercent.locale(locale) |
| .formatDouble(50, status).toString(status); |
| UnicodeString sNoUnitPermille = uNoUnitPermille.locale(locale) |
| .formatDouble(50, status).toString(status); |
| UnicodeString sMeasurePercent = uMeasurePercent.locale(locale) |
| .formatDouble(50, status).toString(status); |
| UnicodeString sMeasurePermille = uMeasurePermille.locale(locale) |
| .formatDouble(50, status).toString(status); |
| |
| assertEquals(u"Percent, locale " + UnicodeString(locale.getName()), |
| sNoUnitPercent, sMeasurePercent); |
| assertEquals(u"Permille, locale " + UnicodeString(locale.getName()), |
| sNoUnitPermille, sMeasurePermille); |
| } |
| } |
| |
| void NumberFormatterApiTest::roundingFraction() { |
| assertFormatDescending( |
| u"Integer", |
| u"precision-integer", |
| u".", |
| NumberFormatter::with().precision(Precision::integer()), |
| Locale::getEnglish(), |
| u"87,650", |
| u"8,765", |
| u"876", |
| u"88", |
| u"9", |
| u"1", |
| u"0", |
| u"0", |
| u"0"); |
| |
| assertFormatDescending( |
| u"Fixed Fraction", |
| u".000", |
| u".000", |
| NumberFormatter::with().precision(Precision::fixedFraction(3)), |
| Locale::getEnglish(), |
| u"87,650.000", |
| u"8,765.000", |
| u"876.500", |
| u"87.650", |
| u"8.765", |
| u"0.876", |
| u"0.088", |
| u"0.009", |
| u"0.000"); |
| |
| assertFormatDescending( |
| u"Min Fraction", |
| u".0*", |
| u".0+", |
| NumberFormatter::with().precision(Precision::minFraction(1)), |
| Locale::getEnglish(), |
| u"87,650.0", |
| u"8,765.0", |
| u"876.5", |
| u"87.65", |
| u"8.765", |
| u"0.8765", |
| u"0.08765", |
| u"0.008765", |
| u"0.0"); |
| |
| assertFormatDescending( |
| u"Max Fraction", |
| u".#", |
| u".#", |
| NumberFormatter::with().precision(Precision::maxFraction(1)), |
| Locale::getEnglish(), |
| u"87,650", |
| u"8,765", |
| u"876.5", |
| u"87.6", |
| u"8.8", |
| u"0.9", |
| u"0.1", |
| u"0", |
| u"0"); |
| |
| assertFormatDescending( |
| u"Min/Max Fraction", |
| u".0##", |
| u".0##", |
| NumberFormatter::with().precision(Precision::minMaxFraction(1, 3)), |
| Locale::getEnglish(), |
| u"87,650.0", |
| u"8,765.0", |
| u"876.5", |
| u"87.65", |
| u"8.765", |
| u"0.876", |
| u"0.088", |
| u"0.009", |
| u"0.0"); |
| } |
| |
| void NumberFormatterApiTest::roundingFigures() { |
| assertFormatSingle( |
| u"Fixed Significant", |
| u"@@@", |
| u"@@@", |
| NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)), |
| Locale::getEnglish(), |
| -98, |
| u"-98.0"); |
| |
| assertFormatSingle( |
| u"Fixed Significant Rounding", |
| u"@@@", |
| u"@@@", |
| NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)), |
| Locale::getEnglish(), |
| -98.7654321, |
| u"-98.8"); |
| |
| assertFormatSingle( |
| u"Fixed Significant Zero", |
| u"@@@", |
| u"@@@", |
| NumberFormatter::with().precision(Precision::fixedSignificantDigits(3)), |
| Locale::getEnglish(), |
| 0, |
| u"0.00"); |
| |
| assertFormatSingle( |
| u"Min Significant", |
| u"@@*", |
| u"@@+", |
| NumberFormatter::with().precision(Precision::minSignificantDigits(2)), |
| Locale::getEnglish(), |
| -9, |
| u"-9.0"); |
| |
| assertFormatSingle( |
| u"Max Significant", |
| u"@###", |
| u"@###", |
| NumberFormatter::with().precision(Precision::maxSignificantDigits(4)), |
| Locale::getEnglish(), |
| 98.7654321, |
| u"98.77"); |
| |
| assertFormatSingle( |
| u"Min/Max Significant", |
| u"@@@#", |
| u"@@@#", |
| NumberFormatter::with().precision(Precision::minMaxSignificantDigits(3, 4)), |
| Locale::getEnglish(), |
| 9.99999, |
| u"10.0"); |
| |
| assertFormatSingle( |
| u"Fixed Significant on zero with lots of integer width", |
| u"@ integer-width/+000", |
| u"@ 000", |
| NumberFormatter::with().precision(Precision::fixedSignificantDigits(1)) |
| .integerWidth(IntegerWidth::zeroFillTo(3)), |
| Locale::getEnglish(), |
| 0, |
| "000"); |
| |
| assertFormatSingle( |
| u"Fixed Significant on zero with zero integer width", |
| u"@ integer-width/*", |
| u"@ integer-width/+", |
| NumberFormatter::with().precision(Precision::fixedSignificantDigits(1)) |
| .integerWidth(IntegerWidth::zeroFillTo(0)), |
| Locale::getEnglish(), |
| 0, |
| "0"); |
| } |
| |
| void NumberFormatterApiTest::roundingFractionFigures() { |
| assertFormatDescending( |
| u"Basic Significant", // for comparison |
| u"@#", |
| u"@#", |
| NumberFormatter::with().precision(Precision::maxSignificantDigits(2)), |
| Locale::getEnglish(), |
| u"88,000", |
| u"8,800", |
| u"880", |
| u"88", |
| u"8.8", |
| u"0.88", |
| u"0.088", |
| u"0.0088", |
| u"0"); |
| |
| assertFormatDescending( |
| u"FracSig minMaxFrac minSig", |
| u".0#/@@@*", |
| u".0#/@@@+", |
| NumberFormatter::with().precision(Precision::minMaxFraction(1, 2).withMinDigits(3)), |
| Locale::getEnglish(), |
| u"87,650.0", |
| u"8,765.0", |
| u"876.5", |
| u"87.65", |
| u"8.76", |
| u"0.876", // minSig beats maxFrac |
| u"0.0876", // minSig beats maxFrac |
| u"0.00876", // minSig beats maxFrac |
| u"0.0"); |
| |
| assertFormatDescending( |
| u"FracSig minMaxFrac maxSig A", |
| u".0##/@#", |
| u".0##/@#", |
| NumberFormatter::with().precision(Precision::minMaxFraction(1, 3).withMaxDigits(2)), |
| Locale::getEnglish(), |
| u"88,000.0", // maxSig beats maxFrac |
| u"8,800.0", // maxSig beats maxFrac |
| u"880.0", // maxSig beats maxFrac |
| u"88.0", // maxSig beats maxFrac |
| u"8.8", // maxSig beats maxFrac |
| u"0.88", // maxSig beats maxFrac |
| u"0.088", |
| u"0.009", |
| u"0.0"); |
| |
| assertFormatDescending( |
| u"FracSig minMaxFrac maxSig B", |
| u".00/@#", |
| u".00/@#", |
| NumberFormatter::with().precision(Precision::fixedFraction(2).withMaxDigits(2)), |
| Locale::getEnglish(), |
| u"88,000.00", // maxSig beats maxFrac |
| u"8,800.00", // maxSig beats maxFrac |
| u"880.00", // maxSig beats maxFrac |
| u"88.00", // maxSig beats maxFrac |
| u"8.80", // maxSig beats maxFrac |
| u"0.88", |
| u"0.09", |
| u"0.01", |
| u"0.00"); |
| |
| assertFormatSingle( |
| u"FracSig with trailing zeros A", |
| u".00/@@@*", |
| u".00/@@@+", |
| NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)), |
| Locale::getEnglish(), |
| 0.1, |
| u"0.10"); |
| |
| assertFormatSingle( |
| u"FracSig with trailing zeros B", |
| u".00/@@@*", |
| u".00/@@@+", |
| NumberFormatter::with().precision(Precision::fixedFraction(2).withMinDigits(3)), |
| Locale::getEnglish(), |
| 0.0999999, |
| u"0.10"); |
| } |
| |
| void NumberFormatterApiTest::roundingOther() { |
| assertFormatDescending( |
| u"Rounding None", |
| u"precision-unlimited", |
| u".+", |
| NumberFormatter::with().precision(Precision::unlimited()), |
| Locale::getEnglish(), |
| u"87,650", |
| u"8,765", |
| u"876.5", |
| u"87.65", |
| u"8.765", |
| u"0.8765", |
| u"0.08765", |
| u"0.008765", |
| u"0"); |
| |
| assertFormatDescending( |
| u"Increment", |
| u"precision-increment/0.5", |
| u"precision-increment/0.5", |
| NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(1)), |
| Locale::getEnglish(), |
| u"87,650.0", |
| u"8,765.0", |
| u"876.5", |
| u"87.5", |
| u"9.0", |
| u"1.0", |
| u"0.0", |
| u"0.0", |
| u"0.0"); |
| |
| assertFormatDescending( |
| u"Increment with Min Fraction", |
| u"precision-increment/0.50", |
| u"precision-increment/0.50", |
| NumberFormatter::with().precision(Precision::increment(0.5).withMinFraction(2)), |
| Locale::getEnglish(), |
| u"87,650.00", |
| u"8,765.00", |
| u"876.50", |
| u"87.50", |
| u"9.00", |
| u"1.00", |
| u"0.00", |
| u"0.00", |
| u"0.00"); |
| |
| assertFormatDescending( |
| u"Strange Increment", |
| u"precision-increment/3.140", |
| u"precision-increment/3.140", |
| NumberFormatter::with().precision(Precision::increment(3.14).withMinFraction(3)), |
| Locale::getEnglish(), |
| u"87,649.960", |
| u"8,763.740", |
| u"876.060", |
| u"87.920", |
| u"9.420", |
| u"0.000", |
| u"0.000", |
| u"0.000", |
| u"0.000"); |
| |
| assertFormatDescending( |
| u"Increment Resolving to Power of 10", |
| u"precision-increment/0.010", |
| u"precision-increment/0.010", |
| NumberFormatter::with().precision(Precision::increment(0.01).withMinFraction(3)), |
| Locale::getEnglish(), |
| u"87,650.000", |
| u"8,765.000", |
| u"876.500", |
| u"87.650", |
| u"8.760", |
| u"0.880", |
| u"0.090", |
| u"0.010", |
| u"0.000"); |
| |
| assertFormatDescending( |
| u"Currency Standard", |
| u"currency/CZK precision-currency-standard", |
| u"currency/CZK precision-currency-standard", |
| NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_STANDARD)) |
| .unit(CZK), |
| Locale::getEnglish(), |
| u"CZK 87,650.00", |
| u"CZK 8,765.00", |
| u"CZK 876.50", |
| u"CZK 87.65", |
| u"CZK 8.76", |
| u"CZK 0.88", |
| u"CZK 0.09", |
| u"CZK 0.01", |
| u"CZK 0.00"); |
| |
| assertFormatDescending( |
| u"Currency Cash", |
| u"currency/CZK precision-currency-cash", |
| u"currency/CZK precision-currency-cash", |
| NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH)) |
| .unit(CZK), |
| Locale::getEnglish(), |
| u"CZK 87,650", |
| u"CZK 8,765", |
| u"CZK 876", |
| u"CZK 88", |
| u"CZK 9", |
| u"CZK 1", |
| u"CZK 0", |
| u"CZK 0", |
| u"CZK 0"); |
| |
| assertFormatDescending( |
| u"Currency Cash with Nickel Rounding", |
| u"currency/CAD precision-currency-cash", |
| u"currency/CAD precision-currency-cash", |
| NumberFormatter::with().precision(Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH)) |
| .unit(CAD), |
| Locale::getEnglish(), |
| u"CA$87,650.00", |
| u"CA$8,765.00", |
| u"CA$876.50", |
| u"CA$87.65", |
| u"CA$8.75", |
| u"CA$0.90", |
| u"CA$0.10", |
| u"CA$0.00", |
| u"CA$0.00"); |
| |
| assertFormatDescending( |
| u"Currency not in top-level fluent chain", |
| u"precision-integer", // calling .withCurrency() applies currency rounding rules immediately |
| u".", |
| NumberFormatter::with().precision( |
| Precision::currency(UCurrencyUsage::UCURR_USAGE_CASH).withCurrency(CZK)), |
| Locale::getEnglish(), |
| u"87,650", |
| u"8,765", |
| u"876", |
| u"88", |
| u"9", |
| u"1", |
| u"0", |
| u"0", |
| u"0"); |
| |
| // NOTE: Other tests cover the behavior of the other rounding modes. |
| assertFormatDescending( |
| u"Rounding Mode CEILING", |
| u"precision-integer rounding-mode-ceiling", |
| u". rounding-mode-ceiling", |
| NumberFormatter::with().precision(Precision::integer()).roundingMode(UNUM_ROUND_CEILING), |
| Locale::getEnglish(), |
| u"87,650", |
| u"8,765", |
| u"877", |
| u"88", |
| u"9", |
| u"1", |
| u"1", |
| u"1", |
| u"0"); |
| |
| assertFormatSingle( |
| u"ICU-20974 Double.MIN_NORMAL", |
| u"scientific", |
| u"E0", |
| NumberFormatter::with().notation(Notation::scientific()), |
| Locale::getEnglish(), |
| DBL_MIN, |
| u"2.225074E-308"); |
| |
| #ifndef DBL_TRUE_MIN |
| #define DBL_TRUE_MIN 4.9E-324 |
| #endif |
| |
| // Note: this behavior is intentionally different from Java; see |
| // https://github.com/google/double-conversion/issues/126 |
| assertFormatSingle( |
| u"ICU-20974 Double.MIN_VALUE", |
| u"scientific", |
| u"E0", |
| NumberFormatter::with().notation(Notation::scientific()), |
| Locale::getEnglish(), |
| DBL_TRUE_MIN, |
| u"5E-324"); |
| } |
| |
| void NumberFormatterApiTest::grouping() { |
| assertFormatDescendingBig( |
| u"Western Grouping", |
| u"group-auto", |
| u"", |
| NumberFormatter::with().grouping(UNUM_GROUPING_AUTO), |
| Locale::getEnglish(), |
| u"87,650,000", |
| u"8,765,000", |
| u"876,500", |
| u"87,650", |
| u"8,765", |
| u"876.5", |
| u"87.65", |
| u"8.765", |
| u"0"); |
| |
| assertFormatDescendingBig( |
| u"Indic Grouping", |
| u"group-auto", |
| u"", |
| NumberFormatter::with().grouping(UNUM_GROUPING_AUTO), |
| Locale("en-IN"), |
| u"8,76,50,000", |
| u"87,65,000", |
| u"8,76,500", |
| u"87,650", |
| u"8,765", |
| u"876.5", |
| u"87.65", |
| u"8.765", |
| u"0"); |
| |
| assertFormatDescendingBig( |
| u"Western Grouping, Min 2", |
| u"group-min2", |
| u",?", |
| NumberFormatter::with().grouping(UNUM_GROUPING_MIN2), |
| Locale::getEnglish(), |
| u"87,650,000", |
| u"8,765,000", |
| u"876,500", |
| u"87,650", |
| u"8765", |
| u"876.5", |
| u"87.65", |
| u"8.765", |
| u"0"); |
| |
| assertFormatDescendingBig( |
| u"Indic Grouping, Min 2", |
| u"group-min2", |
| u",?", |
| NumberFormatter::with().grouping(UNUM_GROUPING_MIN2), |
| Locale("en-IN"), |
| u"8,76,50,000", |
| u"87,65,000", |
| u"8,76,500", |
| u"87,650", |
| u"8765", |
| u"876.5", |
| u"87.65", |
| u"8.765", |
| u"0"); |
| |
| assertFormatDescendingBig( |
| u"No Grouping", |
| u"group-off", |
| u",_", |
| NumberFormatter::with().grouping(UNUM_GROUPING_OFF), |
| Locale("en-IN"), |
| u"87650000", |
| u"8765000", |
| u"876500", |
| u"87650", |
| u"8765", |
| u"876.5", |
| u"87.65", |
| u"8.765", |
| u"0"); |
| |
| assertFormatDescendingBig( |
| u"Indic locale with THOUSANDS grouping", |
| u"group-thousands", |
| u"group-thousands", |
| NumberFormatter::with().grouping(UNUM_GROUPING_THOUSANDS), |
| Locale("en-IN"), |
| u"87,650,000", |
| u"8,765,000", |
| u"876,500", |
| u"87,650", |
| u"8,765", |
| u"876.5", |
| u"87.65", |
| u"8.765", |
| u"0"); |
| |
| // NOTE: Polish is interesting because it has minimumGroupingDigits=2 in locale data |
| // (Most locales have either 1 or 2) |
| // If this test breaks due to data changes, find another locale that has minimumGroupingDigits. |
| assertFormatDescendingBig( |
| u"Polish Grouping", |
| u"group-auto", |
| u"", |
| NumberFormatter::with().grouping(UNUM_GROUPING_AUTO), |
| Locale("pl"), |
| u"87 650 000", |
| u"8 765 000", |
| u"876 500", |
| u"87 650", |
| u"8765", |
| u"876,5", |
| u"87,65", |
| u"8,765", |
| u"0"); |
| |
| assertFormatDescendingBig( |
| u"Polish Grouping, Min 2", |
| u"group-min2", |
| u",?", |
| NumberFormatter::with().grouping(UNUM_GROUPING_MIN2), |
| Locale("pl"), |
| u"87 650 000", |
| u"8 765 000", |
| u"876 500", |
| u"87 650", |
| u"8765", |
| u"876,5", |
| u"87,65", |
| u"8,765", |
| u"0"); |
| |
| assertFormatDescendingBig( |
| u"Polish Grouping, Always", |
| u"group-on-aligned", |
| u",!", |
| NumberFormatter::with().grouping(UNUM_GROUPING_ON_ALIGNED), |
| Locale("pl"), |
| u"87 650 000", |
| u"8 765 000", |
| u"876 500", |
| u"87 650", |
| u"8 765", |
| u"876,5", |
| u"87,65", |
| u"8,765", |
| u"0"); |
| |
| // NOTE: Bulgarian is interesting because it has no grouping in the default currency format. |
| // If this test breaks due to data changes, find another locale that has no default grouping. |
| assertFormatDescendingBig( |
| u"Bulgarian Currency Grouping", |
| u"currency/USD group-auto", |
| u"currency/USD", |
| NumberFormatter::with().grouping(UNUM_GROUPING_AUTO).unit(USD), |
| Locale("bg"), |
| u"87650000,00 щ.д.", |
| u"8765000,00 щ.д.", |
| u"876500,00 щ.д.", |
| u"87650,00 щ.д.", |
| u"8765,00 щ.д.", |
| u"876,50 щ.д.", |
| u"87,65 щ.д.", |
| u"8,76 щ.д.", |
| u"0,00 щ.д."); |
| |
| assertFormatDescendingBig( |
| u"Bulgarian Currency Grouping, Always", |
|