ICU-20418 Implementing concise number skeletons in ICU4J.
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/StringSegment.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/StringSegment.java
index ce3f667..da5da54 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/impl/StringSegment.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/StringSegment.java
@@ -221,8 +221,14 @@
return Utility.charSequenceHashCode(this);
}
+ /** Returns a string representation useful for debugging. */
@Override
public String toString() {
return str.substring(0, start) + "[" + str.substring(start, end) + "]" + str.substring(end);
}
+
+ /** Returns a String that is equivalent to the CharSequence representation. */
+ public String asString() {
+ return str.substring(start, end);
+ }
}
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java
index a6878a1..492f6b5 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java
@@ -53,6 +53,7 @@
STATE_INCREMENT_PRECISION,
STATE_MEASURE_UNIT,
STATE_PER_MEASURE_UNIT,
+ STATE_IDENTIFIER_UNIT,
STATE_CURRENCY_UNIT,
STATE_INTEGER_WIDTH,
STATE_NUMBERING_SYSTEM,
@@ -76,6 +77,7 @@
STEM_BASE_UNIT,
STEM_PERCENT,
STEM_PERMILLE,
+ STEM_PERCENT_100, // concise-only
STEM_PRECISION_INTEGER,
STEM_PRECISION_UNLIMITED,
STEM_PRECISION_CURRENCY_STANDARD,
@@ -113,6 +115,7 @@
STEM_PRECISION_INCREMENT,
STEM_MEASURE_UNIT,
STEM_PER_MEASURE_UNIT,
+ STEM_UNIT,
STEM_CURRENCY,
STEM_INTEGER_WIDTH,
STEM_NUMBERING_SYSTEM,
@@ -174,11 +177,27 @@
b.add("precision-increment", StemEnum.STEM_PRECISION_INCREMENT.ordinal());
b.add("measure-unit", StemEnum.STEM_MEASURE_UNIT.ordinal());
b.add("per-measure-unit", StemEnum.STEM_PER_MEASURE_UNIT.ordinal());
+ b.add("unit", StemEnum.STEM_UNIT.ordinal());
b.add("currency", StemEnum.STEM_CURRENCY.ordinal());
b.add("integer-width", StemEnum.STEM_INTEGER_WIDTH.ordinal());
b.add("numbering-system", StemEnum.STEM_NUMBERING_SYSTEM.ordinal());
b.add("scale", StemEnum.STEM_SCALE.ordinal());
+ // Section 3 (concise tokens):
+ b.add("K", StemEnum.STEM_COMPACT_SHORT.ordinal());
+ b.add("KK", StemEnum.STEM_COMPACT_LONG.ordinal());
+ b.add("%", StemEnum.STEM_PERCENT.ordinal());
+ b.add("%x100", StemEnum.STEM_PERCENT_100.ordinal());
+ b.add(",_", StemEnum.STEM_GROUP_OFF.ordinal());
+ b.add(",?", StemEnum.STEM_GROUP_MIN2.ordinal());
+ b.add(",!", StemEnum.STEM_GROUP_ON_ALIGNED.ordinal());
+ b.add("+!", StemEnum.STEM_SIGN_ALWAYS.ordinal());
+ b.add("+_", StemEnum.STEM_SIGN_NEVER.ordinal());
+ b.add("()", StemEnum.STEM_SIGN_ACCOUNTING.ordinal());
+ b.add("()!", StemEnum.STEM_SIGN_ACCOUNTING_ALWAYS.ordinal());
+ b.add("+?", StemEnum.STEM_SIGN_EXCEPT_ZERO.ordinal());
+ b.add("()?", StemEnum.STEM_SIGN_ACCOUNTING_EXCEPT_ZERO.ordinal());
+
// Build the CharsTrie
// TODO: Use SLOW or FAST here?
return b.buildCharSequence(StringTrieBuilder.Option.FAST).toString();
@@ -604,6 +623,14 @@
checkNull(macros.precision, segment);
BlueprintHelpers.parseDigitsStem(segment, macros);
return ParseState.STATE_NULL;
+ case 'E':
+ checkNull(macros.notation, segment);
+ BlueprintHelpers.parseScientificStem(segment, macros);
+ return ParseState.STATE_NULL;
+ case '0':
+ checkNull(macros.notation, segment);
+ BlueprintHelpers.parseIntegerStem(segment, macros);
+ return ParseState.STATE_NULL;
}
// Now look at the stemsTrie, which is already be pointing at our stem.
@@ -641,6 +668,13 @@
macros.unit = StemToObject.unit(stem);
return ParseState.STATE_NULL;
+ case STEM_PERCENT_100:
+ checkNull(macros.scale, segment);
+ checkNull(macros.unit, segment);
+ macros.scale = Scale.powerOfTen(2);
+ macros.unit = NoUnit.PERCENT;
+ return ParseState.STATE_NULL;
+
case STEM_PRECISION_INTEGER:
case STEM_PRECISION_UNLIMITED:
case STEM_PRECISION_CURRENCY_STANDARD:
@@ -720,6 +754,11 @@
checkNull(macros.perUnit, segment);
return ParseState.STATE_PER_MEASURE_UNIT;
+ case STEM_UNIT:
+ checkNull(macros.unit, segment);
+ checkNull(macros.perUnit, segment);
+ return ParseState.STATE_IDENTIFIER_UNIT;
+
case STEM_CURRENCY:
checkNull(macros.unit, segment);
return ParseState.STATE_CURRENCY_UNIT;
@@ -761,6 +800,9 @@
case STATE_PER_MEASURE_UNIT:
BlueprintHelpers.parseMeasurePerUnitOption(segment, macros);
return ParseState.STATE_NULL;
+ case STATE_IDENTIFIER_UNIT:
+ BlueprintHelpers.parseIdentifierUnitOption(segment, macros);
+ return ParseState.STATE_NULL;
case STATE_INCREMENT_PRECISION:
BlueprintHelpers.parseIncrementOption(segment, macros);
return ParseState.STATE_NULL;
@@ -970,7 +1012,7 @@
}
private static void parseMeasurePerUnitOption(StringSegment segment, MacroProps macros) {
- // A little bit of a hack: safe the current unit (numerator), call the main measure unit
+ // A little bit of a hack: save the current unit (numerator), call the main measure unit
// parsing code, put back the numerator unit, and put the new unit into per-unit.
MeasureUnit numerator = macros.unit;
parseMeasureUnitOption(segment, macros);
@@ -978,6 +1020,17 @@
macros.unit = numerator;
}
+ private static void parseIdentifierUnitOption(StringSegment segment, MacroProps macros) {
+ MeasureUnit[] units = MeasureUnit.parseCoreUnitIdentifier(segment.asString());
+ if (units == null) {
+ throw new SkeletonSyntaxException("Invalid core unit identifier", segment);
+ }
+ macros.unit = units[0];
+ if (units.length == 2) {
+ macros.perUnit = units[1];
+ }
+ }
+
private static void parseFractionStem(StringSegment segment, MacroProps macros) {
assert segment.charAt(0) == '.';
int offset = 1;
@@ -1012,7 +1065,11 @@
}
// Use the public APIs to enforce bounds checking
if (maxFrac == -1) {
- macros.precision = Precision.minFraction(minFrac);
+ if (minFrac == 0) {
+ macros.precision = Precision.unlimited();
+ } else {
+ macros.precision = Precision.minFraction(minFrac);
+ }
} else {
macros.precision = Precision.minMaxFraction(minFrac, maxFrac);
}
@@ -1081,6 +1138,71 @@
}
}
+ private static void parseScientificStem(StringSegment segment, MacroProps macros) {
+ assert(segment.charAt(0) == 'E');
+ block:
+ {
+ int offset = 1;
+ if (segment.length() == offset) {
+ break block;
+ }
+ boolean isEngineering = false;
+ if (segment.charAt(offset) == 'E') {
+ isEngineering = true;
+ offset++;
+ if (segment.length() == offset) {
+ break block;
+ }
+ }
+ SignDisplay signDisplay = SignDisplay.AUTO;
+ if (segment.charAt(offset) == '+') {
+ offset++;
+ if (segment.length() == offset) {
+ break block;
+ }
+ if (segment.charAt(offset) == '!') {
+ signDisplay = SignDisplay.ALWAYS;
+ } else if (segment.charAt(offset) == '?') {
+ signDisplay = SignDisplay.EXCEPT_ZERO;
+ } else {
+ break block;
+ }
+ offset++;
+ if (segment.length() == offset) {
+ break block;
+ }
+ }
+ int minDigits = 0;
+ for (; offset < segment.length(); offset++) {
+ if (segment.charAt(offset) != '0') {
+ break block;
+ }
+ minDigits++;
+ }
+ macros.notation = (isEngineering ? Notation.engineering() : Notation.scientific())
+ .withExponentSignDisplay(signDisplay)
+ .withMinExponentDigits(minDigits);
+ return;
+ }
+ throw new SkeletonSyntaxException("Invalid scientific stem", segment);
+ }
+
+ private static void parseIntegerStem(StringSegment segment, MacroProps macros) {
+ assert(segment.charAt(0) == '0');
+ int offset = 1;
+ for (; offset < segment.length(); offset++) {
+ if (segment.charAt(offset) != '0') {
+ offset--;
+ break;
+ }
+ }
+ if (offset < segment.length()) {
+ throw new SkeletonSyntaxException("Invalid integer stem", segment);
+ }
+ macros.integerWidth = IntegerWidth.zeroFillTo(offset);
+ return;
+ }
+
/** @return Whether we successfully found and parsed a frac-sig option. */
private static boolean parseFracSigOption(StringSegment segment, MacroProps macros) {
if (segment.charAt(0) != '@') {
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/MeasureUnit.java b/icu4j/main/classes/core/src/com/ibm/icu/util/MeasureUnit.java
index eaf01d5..357fe96 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/util/MeasureUnit.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/util/MeasureUnit.java
@@ -205,6 +205,47 @@
return MeasureUnit.addUnit(type, subType, factory);
}
+ private static MeasureUnit findBySubType(String subType) {
+ populateCache();
+ for (Map<String, MeasureUnit> unitsForType : cache.values()) {
+ if (unitsForType.containsKey(subType)) {
+ return unitsForType.get(subType);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * For ICU use only.
+ * @internal
+ * @deprecated This API is ICU internal only.
+ */
+ @Deprecated
+ public static MeasureUnit[] parseCoreUnitIdentifier(String coreUnitIdentifier) {
+ // First search for the whole code unit identifier as a subType
+ MeasureUnit whole = findBySubType(coreUnitIdentifier);
+ if (whole != null) {
+ return new MeasureUnit[] { whole }; // found a numerator but not denominator
+ }
+
+ // If not found, try breaking apart numerator and denominator
+ int perIdx = coreUnitIdentifier.indexOf("-per-");
+ if (perIdx == -1) {
+ // String does not contain "-per-"
+ return null;
+ }
+ String numeratorStr = coreUnitIdentifier.substring(0, perIdx);
+ String denominatorStr = coreUnitIdentifier.substring(perIdx + 5);
+ MeasureUnit numerator = findBySubType(numeratorStr);
+ MeasureUnit denominator = findBySubType(denominatorStr);
+ if (numerator != null && denominator != null) {
+ return new MeasureUnit[] { numerator, denominator }; // found both a numerator and denominator
+ }
+
+ // The numerator or denominator were invalid
+ return null;
+ }
+
/**
* For ICU use only.
* @internal
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
index a12468f..4ba49dc 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
@@ -72,6 +72,7 @@
assertFormatDescending(
"Basic",
"",
+ "",
NumberFormatter.with(),
ULocale.ENGLISH,
"87,650",
@@ -87,6 +88,7 @@
assertFormatDescendingBig(
"Big Simple",
"notation-simple",
+ "",
NumberFormatter.with().notation(Notation.simple()),
ULocale.ENGLISH,
"87,650,000",
@@ -102,6 +104,7 @@
assertFormatSingle(
"Basic with Negative Sign",
"",
+ "",
NumberFormatter.with(),
ULocale.ENGLISH,
-9876543.21,
@@ -113,6 +116,7 @@
assertFormatDescending(
"Scientific",
"scientific",
+ "E0",
NumberFormatter.with().notation(Notation.scientific()),
ULocale.ENGLISH,
"8.765E4",
@@ -128,6 +132,7 @@
assertFormatDescending(
"Engineering",
"engineering",
+ "EE0",
NumberFormatter.with().notation(Notation.engineering()),
ULocale.ENGLISH,
"87.65E3",
@@ -143,6 +148,7 @@
assertFormatDescending(
"Scientific sign always shown",
"scientific/sign-always",
+ "E+!0",
NumberFormatter.with().notation(Notation.scientific().withExponentSignDisplay(SignDisplay.ALWAYS)),
ULocale.ENGLISH,
"8.765E+4",
@@ -158,6 +164,7 @@
assertFormatDescending(
"Scientific min exponent digits",
"scientific/+ee",
+ "E00",
NumberFormatter.with().notation(Notation.scientific().withMinExponentDigits(2)),
ULocale.ENGLISH,
"8.765E04",
@@ -173,6 +180,7 @@
assertFormatSingle(
"Scientific Negative",
"scientific",
+ "E0",
NumberFormatter.with().notation(Notation.scientific()),
ULocale.ENGLISH,
-1000000,
@@ -181,6 +189,7 @@
assertFormatSingle(
"Scientific Infinity",
"scientific",
+ "E0",
NumberFormatter.with().notation(Notation.scientific()),
ULocale.ENGLISH,
Double.NEGATIVE_INFINITY,
@@ -189,6 +198,7 @@
assertFormatSingle(
"Scientific NaN",
"scientific",
+ "E0",
NumberFormatter.with().notation(Notation.scientific()),
ULocale.ENGLISH,
Double.NaN,
@@ -200,6 +210,7 @@
assertFormatDescendingBig(
"Compact Short",
"compact-short",
+ "K",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
"88M",
@@ -215,6 +226,7 @@
assertFormatDescendingBig(
"Compact Long",
"compact-long",
+ "KK",
NumberFormatter.with().notation(Notation.compactLong()),
ULocale.ENGLISH,
"88 million",
@@ -230,6 +242,7 @@
assertFormatDescending(
"Compact Short Currency",
"compact-short currency/USD",
+ "K currency/USD",
NumberFormatter.with().notation(Notation.compactShort()).unit(USD),
ULocale.ENGLISH,
"$88K",
@@ -245,6 +258,7 @@
assertFormatDescending(
"Compact Short with ISO Currency",
"compact-short currency/USD unit-width-iso-code",
+ "K currency/USD unit-width-iso-code",
NumberFormatter.with().notation(Notation.compactShort()).unit(USD).unitWidth(UnitWidth.ISO_CODE),
ULocale.ENGLISH,
"USD 88K",
@@ -260,6 +274,7 @@
assertFormatDescending(
"Compact Short with Long Name Currency",
"compact-short currency/USD unit-width-full-name",
+ "K currency/USD unit-width-full-name",
NumberFormatter.with().notation(Notation.compactShort()).unit(USD).unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
"88K US dollars",
@@ -277,6 +292,7 @@
assertFormatDescending(
"Compact Long Currency",
"compact-long currency/USD",
+ "KK currency/USD",
NumberFormatter.with().notation(Notation.compactLong()).unit(USD),
ULocale.ENGLISH,
"$88K", // should be something like "$88 thousand"
@@ -294,6 +310,7 @@
assertFormatDescending(
"Compact Long with ISO Currency",
"compact-long currency/USD unit-width-iso-code",
+ "KK currency/USD unit-width-iso-code",
NumberFormatter.with().notation(Notation.compactLong()).unit(USD).unitWidth(UnitWidth.ISO_CODE),
ULocale.ENGLISH,
"USD 88K", // should be something like "USD 88 thousand"
@@ -310,6 +327,7 @@
assertFormatDescending(
"Compact Long with Long Name Currency",
"compact-long currency/USD unit-width-full-name",
+ "KK currency/USD unit-width-full-name",
NumberFormatter.with().notation(Notation.compactLong()).unit(USD).unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
"88 thousand US dollars",
@@ -325,6 +343,7 @@
assertFormatSingle(
"Compact Plural One",
"compact-long",
+ "KK",
NumberFormatter.with().notation(Notation.compactLong()),
ULocale.forLanguageTag("es"),
1000000,
@@ -333,6 +352,7 @@
assertFormatSingle(
"Compact Plural Other",
"compact-long",
+ "KK",
NumberFormatter.with().notation(Notation.compactLong()),
ULocale.forLanguageTag("es"),
2000000,
@@ -341,6 +361,7 @@
assertFormatSingle(
"Compact with Negative Sign",
"compact-short",
+ "K",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
-9876543.21,
@@ -349,6 +370,7 @@
assertFormatSingle(
"Compact Rounding",
"compact-short",
+ "K",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
990000,
@@ -357,6 +379,7 @@
assertFormatSingle(
"Compact Rounding",
"compact-short",
+ "K",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
999000,
@@ -365,6 +388,7 @@
assertFormatSingle(
"Compact Rounding",
"compact-short",
+ "K",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
999900,
@@ -373,6 +397,7 @@
assertFormatSingle(
"Compact Rounding",
"compact-short",
+ "K",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
9900000,
@@ -381,6 +406,7 @@
assertFormatSingle(
"Compact Rounding",
"compact-short",
+ "K",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
9990000,
@@ -389,6 +415,7 @@
assertFormatSingle(
"Compact in zh-Hant-HK",
"compact-short",
+ "K",
NumberFormatter.with().notation(Notation.compactShort()),
new ULocale("zh-Hant-HK"),
1e7,
@@ -397,6 +424,7 @@
assertFormatSingle(
"Compact in zh-Hant",
"compact-short",
+ "K",
NumberFormatter.with().notation(Notation.compactShort()),
new ULocale("zh-Hant"),
1e7,
@@ -405,6 +433,7 @@
assertFormatSingle(
"Compact Infinity",
"compact-short",
+ "K",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
Double.NEGATIVE_INFINITY,
@@ -413,6 +442,7 @@
assertFormatSingle(
"Compact NaN",
"compact-short",
+ "K",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.ENGLISH,
Double.NaN,
@@ -426,6 +456,7 @@
assertFormatSingle(
"Compact Somali No Figure",
null, // feature not supported in skeleton
+ null,
NumberFormatter.with().notation(CompactNotation.forCustomData(compactCustomData)),
ULocale.ENGLISH,
1000,
@@ -437,6 +468,7 @@
assertFormatDescending(
"Meters Short",
"measure-unit/length-meter",
+ "unit/meter",
NumberFormatter.with().unit(MeasureUnit.METER),
ULocale.ENGLISH,
"87,650 m",
@@ -452,6 +484,7 @@
assertFormatDescending(
"Meters Long",
"measure-unit/length-meter unit-width-full-name",
+ "unit/meter unit-width-full-name",
NumberFormatter.with().unit(MeasureUnit.METER).unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
"87,650 meters",
@@ -467,6 +500,7 @@
assertFormatDescending(
"Compact Meters Long",
"compact-long measure-unit/length-meter unit-width-full-name",
+ "KK unit/meter unit-width-full-name",
NumberFormatter.with().notation(Notation.compactLong()).unit(MeasureUnit.METER)
.unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
@@ -483,6 +517,7 @@
assertFormatSingleMeasure(
"Meters with Measure Input",
"unit-width-full-name",
+ "unit-width-full-name",
NumberFormatter.with().unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
new Measure(5.43, MeasureUnit.METER),
@@ -491,6 +526,7 @@
assertFormatSingleMeasure(
"Measure format method takes precedence over fluent chain",
"measure-unit/length-meter",
+ "unit/meter",
NumberFormatter.with().unit(MeasureUnit.METER),
ULocale.ENGLISH,
new Measure(5.43, USD),
@@ -499,6 +535,7 @@
assertFormatSingle(
"Meters with Negative Sign",
"measure-unit/length-meter",
+ "unit/meter",
NumberFormatter.with().unit(MeasureUnit.METER),
ULocale.ENGLISH,
-9876543.21,
@@ -508,6 +545,7 @@
assertFormatSingle(
"Interesting Data Fallback 1",
"measure-unit/duration-day unit-width-full-name",
+ "unit/day unit-width-full-name",
NumberFormatter.with().unit(MeasureUnit.DAY).unitWidth(UnitWidth.FULL_NAME),
ULocale.forLanguageTag("brx"),
5.43,
@@ -517,6 +555,7 @@
assertFormatSingle(
"Interesting Data Fallback 2",
"measure-unit/duration-day unit-width-narrow",
+ "unit/day unit-width-narrow",
NumberFormatter.with().unit(MeasureUnit.DAY).unitWidth(UnitWidth.NARROW),
ULocale.forLanguageTag("brx"),
5.43,
@@ -527,6 +566,7 @@
assertFormatSingle(
"Interesting Data Fallback 3",
"measure-unit/area-square-meter unit-width-narrow",
+ "unit/square-meter unit-width-narrow",
NumberFormatter.with().unit(MeasureUnit.SQUARE_METER).unitWidth(UnitWidth.NARROW),
ULocale.forLanguageTag("en-GB"),
5.43,
@@ -536,6 +576,7 @@
assertFormatSingle(
"Interesting Data Fallback 4",
"measure-unit/area-square-meter unit-width-narrow",
+ "unit/square-meter unit-width-narrow",
NumberFormatter.with().unit(MeasureUnit.SQUARE_METER).unitWidth(UnitWidth.NARROW),
ULocale.forLanguageTag("root"),
5.43,
@@ -546,6 +587,7 @@
assertFormatSingle(
"MeasureUnit Difference between Narrow and Short (Narrow Version)",
"measure-unit/temperature-fahrenheit unit-width-narrow",
+ "unit/fahrenheit unit-width-narrow",
NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT).unitWidth(UnitWidth.NARROW),
ULocale.forLanguageTag("es-US"),
5.43,
@@ -554,6 +596,7 @@
assertFormatSingle(
"MeasureUnit Difference between Narrow and Short (Short Version)",
"measure-unit/temperature-fahrenheit unit-width-short",
+ "unit/fahrenheit unit-width-short",
NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT).unitWidth(UnitWidth.SHORT),
ULocale.forLanguageTag("es-US"),
5.43,
@@ -562,6 +605,7 @@
assertFormatSingle(
"MeasureUnit form without {0} in CLDR pattern",
"measure-unit/temperature-kelvin unit-width-full-name",
+ "unit/kelvin unit-width-full-name",
NumberFormatter.with().unit(MeasureUnit.KELVIN).unitWidth(UnitWidth.FULL_NAME),
ULocale.forLanguageTag("es-MX"),
1,
@@ -570,6 +614,7 @@
assertFormatSingle(
"MeasureUnit form without {0} in CLDR pattern and wide base form",
"measure-unit/temperature-kelvin .00000000000000000000 unit-width-full-name",
+ "unit/kelvin .00000000000000000000 unit-width-full-name",
NumberFormatter.with()
.precision(Precision.fixedFraction(20))
.unit(MeasureUnit.KELVIN)
@@ -584,6 +629,7 @@
assertFormatDescending(
"Meters Per Second Short (unit that simplifies) and perUnit method",
"measure-unit/length-meter per-measure-unit/duration-second",
+ "~unit/meter-per-second", // does not round-trip to the full skeleton above
NumberFormatter.with().unit(MeasureUnit.METER).perUnit(MeasureUnit.SECOND),
ULocale.ENGLISH,
"87,650 m/s",
@@ -599,6 +645,7 @@
assertFormatDescending(
"Pounds Per Square Mile Short (secondary unit has per-format)",
"measure-unit/mass-pound per-measure-unit/area-square-mile",
+ "unit/pound-per-square-mile",
NumberFormatter.with().unit(MeasureUnit.POUND).perUnit(MeasureUnit.SQUARE_MILE),
ULocale.ENGLISH,
"87,650 lb/mi²",
@@ -614,6 +661,7 @@
assertFormatDescending(
"Joules Per Furlong Short (unit with no simplifications or special patterns)",
"measure-unit/energy-joule per-measure-unit/length-furlong",
+ "unit/joule-per-furlong",
NumberFormatter.with().unit(MeasureUnit.JOULE).perUnit(MeasureUnit.FURLONG),
ULocale.ENGLISH,
"87,650 J/fur",
@@ -632,6 +680,7 @@
assertFormatDescending(
"Currency",
"currency/GBP",
+ "currency/GBP",
NumberFormatter.with().unit(GBP),
ULocale.ENGLISH,
"£87,650.00",
@@ -647,6 +696,7 @@
assertFormatDescending(
"Currency ISO",
"currency/GBP unit-width-iso-code",
+ "currency/GBP unit-width-iso-code",
NumberFormatter.with().unit(GBP).unitWidth(UnitWidth.ISO_CODE),
ULocale.ENGLISH,
"GBP 87,650.00",
@@ -662,6 +712,7 @@
assertFormatDescending(
"Currency Long Name",
"currency/GBP unit-width-full-name",
+ "currency/GBP unit-width-full-name",
NumberFormatter.with().unit(GBP).unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
"87,650.00 British pounds",
@@ -677,6 +728,7 @@
assertFormatDescending(
"Currency Hidden",
"currency/GBP unit-width-hidden",
+ "currency/GBP unit-width-hidden",
NumberFormatter.with().unit(GBP).unitWidth(UnitWidth.HIDDEN),
ULocale.ENGLISH,
"87,650.00",
@@ -692,6 +744,7 @@
assertFormatSingleMeasure(
"Currency with CurrencyAmount Input",
"",
+ "",
NumberFormatter.with(),
ULocale.ENGLISH,
new CurrencyAmount(5.43, GBP),
@@ -700,6 +753,7 @@
assertFormatSingle(
"Currency Long Name from Pattern Syntax",
null,
+ null,
NumberFormatter.fromDecimalFormat(
PatternStringParser.parseToProperties("0 ¤¤¤"),
DecimalFormatSymbols.getInstance(ULocale.ENGLISH),
@@ -711,6 +765,7 @@
assertFormatSingle(
"Currency with Negative Sign",
"currency/GBP",
+ "currency/GBP",
NumberFormatter.with().unit(GBP),
ULocale.ENGLISH,
-9876543.21,
@@ -721,6 +776,7 @@
assertFormatSingle(
"Currency Difference between Narrow and Short (Narrow Version)",
"currency/USD unit-width-narrow",
+ "currency/USD unit-width-narrow",
NumberFormatter.with().unit(USD).unitWidth(UnitWidth.NARROW),
ULocale.forLanguageTag("en-CA"),
5.43,
@@ -729,6 +785,7 @@
assertFormatSingle(
"Currency Difference between Narrow and Short (Short Version)",
"currency/USD unit-width-short",
+ "currency/USD unit-width-short",
NumberFormatter.with().unit(USD).unitWidth(UnitWidth.SHORT),
ULocale.forLanguageTag("en-CA"),
5.43,
@@ -737,6 +794,7 @@
assertFormatSingle(
"Currency-dependent format (Control)",
"currency/USD unit-width-short",
+ "currency/USD unit-width-short",
NumberFormatter.with().unit(USD).unitWidth(UnitWidth.SHORT),
ULocale.forLanguageTag("ca"),
444444.55,
@@ -745,6 +803,7 @@
assertFormatSingle(
"Currency-dependent format (Test)",
"currency/ESP unit-width-short",
+ "currency/ESP unit-width-short",
NumberFormatter.with().unit(ESP).unitWidth(UnitWidth.SHORT),
ULocale.forLanguageTag("ca"),
444444.55,
@@ -753,6 +812,7 @@
assertFormatSingle(
"Currency-dependent symbols (Control)",
"currency/USD unit-width-short",
+ "currency/USD unit-width-short",
NumberFormatter.with().unit(USD).unitWidth(UnitWidth.SHORT),
ULocale.forLanguageTag("pt-PT"),
444444.55,
@@ -763,6 +823,7 @@
assertFormatSingle(
"Currency-dependent symbols (Test Short)",
"currency/PTE unit-width-short",
+ "currency/PTE unit-width-short",
NumberFormatter.with().unit(PTE).unitWidth(UnitWidth.SHORT),
ULocale.forLanguageTag("pt-PT"),
444444.55,
@@ -771,6 +832,7 @@
assertFormatSingle(
"Currency-dependent symbols (Test Narrow)",
"currency/PTE unit-width-narrow",
+ "currency/PTE unit-width-narrow",
NumberFormatter.with().unit(PTE).unitWidth(UnitWidth.NARROW),
ULocale.forLanguageTag("pt-PT"),
444444.55,
@@ -779,6 +841,7 @@
assertFormatSingle(
"Currency-dependent symbols (Test ISO Code)",
"currency/PTE unit-width-iso-code",
+ "currency/PTE unit-width-iso-code",
NumberFormatter.with().unit(PTE).unitWidth(UnitWidth.ISO_CODE),
ULocale.forLanguageTag("pt-PT"),
444444.55,
@@ -787,6 +850,7 @@
assertFormatSingle(
"Plural form depending on visible digits (ICU-20499)",
"currency/RON unit-width-full-name",
+ "currency/RON unit-width-full-name",
NumberFormatter.with().unit(RON).unitWidth(UnitWidth.FULL_NAME),
ULocale.forLanguageTag("ro-RO"),
24,
@@ -798,6 +862,7 @@
assertFormatDescending(
"Percent",
"percent",
+ "%",
NumberFormatter.with().unit(NoUnit.PERCENT),
ULocale.ENGLISH,
"87,650%",
@@ -813,6 +878,7 @@
assertFormatDescending(
"Permille",
"permille",
+ "permille",
NumberFormatter.with().unit(NoUnit.PERMILLE),
ULocale.ENGLISH,
"87,650‰",
@@ -828,6 +894,7 @@
assertFormatSingle(
"NoUnit Base",
"base-unit",
+ "",
NumberFormatter.with().unit(NoUnit.BASE),
ULocale.ENGLISH,
51423,
@@ -836,6 +903,7 @@
assertFormatSingle(
"Percent with Negative Sign",
"percent",
+ "%",
NumberFormatter.with().unit(NoUnit.PERCENT),
ULocale.ENGLISH,
-98.7654321,
@@ -847,6 +915,7 @@
assertFormatDescending(
"Integer",
"precision-integer",
+ ".",
NumberFormatter.with().precision(Precision.integer()),
ULocale.ENGLISH,
"87,650",
@@ -862,6 +931,7 @@
assertFormatDescending(
"Fixed Fraction",
".000",
+ ".000",
NumberFormatter.with().precision(Precision.fixedFraction(3)),
ULocale.ENGLISH,
"87,650.000",
@@ -877,6 +947,7 @@
assertFormatDescending(
"Min Fraction",
".0+",
+ ".0+",
NumberFormatter.with().precision(Precision.minFraction(1)),
ULocale.ENGLISH,
"87,650.0",
@@ -892,6 +963,7 @@
assertFormatDescending(
"Max Fraction",
".#",
+ ".#",
NumberFormatter.with().precision(Precision.maxFraction(1)),
ULocale.ENGLISH,
"87,650",
@@ -907,6 +979,7 @@
assertFormatDescending(
"Min/Max Fraction",
".0##",
+ ".0##",
NumberFormatter.with().precision(Precision.minMaxFraction(1, 3)),
ULocale.ENGLISH,
"87,650.0",
@@ -925,6 +998,7 @@
assertFormatSingle(
"Fixed Significant",
"@@@",
+ "@@@",
NumberFormatter.with().precision(Precision.fixedSignificantDigits(3)),
ULocale.ENGLISH,
-98,
@@ -933,6 +1007,7 @@
assertFormatSingle(
"Fixed Significant Rounding",
"@@@",
+ "@@@",
NumberFormatter.with().precision(Precision.fixedSignificantDigits(3)),
ULocale.ENGLISH,
-98.7654321,
@@ -941,6 +1016,7 @@
assertFormatSingle(
"Fixed Significant Zero",
"@@@",
+ "@@@",
NumberFormatter.with().precision(Precision.fixedSignificantDigits(3)),
ULocale.ENGLISH,
0,
@@ -949,6 +1025,7 @@
assertFormatSingle(
"Min Significant",
"@@+",
+ "@@+",
NumberFormatter.with().precision(Precision.minSignificantDigits(2)),
ULocale.ENGLISH,
-9,
@@ -957,6 +1034,7 @@
assertFormatSingle(
"Max Significant",
"@###",
+ "@###",
NumberFormatter.with().precision(Precision.maxSignificantDigits(4)),
ULocale.ENGLISH,
98.7654321,
@@ -965,6 +1043,7 @@
assertFormatSingle(
"Min/Max Significant",
"@@@#",
+ "@@@#",
NumberFormatter.with().precision(Precision.minMaxSignificantDigits(3, 4)),
ULocale.ENGLISH,
9.99999,
@@ -973,6 +1052,7 @@
assertFormatSingle(
"Fixed Significant on zero with zero integer width",
"@ integer-width/+",
+ "@ integer-width/+",
NumberFormatter.with().precision(Precision.fixedSignificantDigits(1)).integerWidth(IntegerWidth.zeroFillTo(0)),
ULocale.ENGLISH,
0,
@@ -981,6 +1061,7 @@
assertFormatSingle(
"Fixed Significant on zero with lots of integer width",
"@ integer-width/+000",
+ "@ 000",
NumberFormatter.with().precision(Precision.fixedSignificantDigits(1)).integerWidth(IntegerWidth.zeroFillTo(3)),
ULocale.ENGLISH,
0,
@@ -992,6 +1073,7 @@
assertFormatDescending(
"Basic Significant", // for comparison
"@#",
+ "@#",
NumberFormatter.with().precision(Precision.maxSignificantDigits(2)),
ULocale.ENGLISH,
"88,000",
@@ -1007,6 +1089,7 @@
assertFormatDescending(
"FracSig minMaxFrac minSig",
".0#/@@@+",
+ ".0#/@@@+",
NumberFormatter.with().precision(Precision.minMaxFraction(1, 2).withMinDigits(3)),
ULocale.ENGLISH,
"87,650.0",
@@ -1022,6 +1105,7 @@
assertFormatDescending(
"FracSig minMaxFrac maxSig A",
".0##/@#",
+ ".0##/@#",
NumberFormatter.with().precision(Precision.minMaxFraction(1, 3).withMaxDigits(2)),
ULocale.ENGLISH,
"88,000.0", // maxSig beats maxFrac
@@ -1037,6 +1121,7 @@
assertFormatDescending(
"FracSig minMaxFrac maxSig B",
".00/@#",
+ ".00/@#",
NumberFormatter.with().precision(Precision.fixedFraction(2).withMaxDigits(2)),
ULocale.ENGLISH,
"88,000.00", // maxSig beats maxFrac
@@ -1052,6 +1137,7 @@
assertFormatDescending(
"FracSig minFrac maxSig",
".0+/@#",
+ ".0+/@#",
NumberFormatter.with().precision(Precision.minFraction(1).withMaxDigits(2)),
ULocale.ENGLISH,
"88,000.0",
@@ -1067,6 +1153,7 @@
assertFormatSingle(
"FracSig with trailing zeros A",
".00/@@@+",
+ ".00/@@@+",
NumberFormatter.with().precision(Precision.fixedFraction(2).withMinDigits(3)),
ULocale.ENGLISH,
0.1,
@@ -1075,6 +1162,7 @@
assertFormatSingle(
"FracSig with trailing zeros B",
".00/@@@+",
+ ".00/@@@+",
NumberFormatter.with().precision(Precision.fixedFraction(2).withMinDigits(3)),
ULocale.ENGLISH,
0.0999999,
@@ -1086,6 +1174,7 @@
assertFormatDescending(
"Rounding None",
"precision-unlimited",
+ ".+",
NumberFormatter.with().precision(Precision.unlimited()),
ULocale.ENGLISH,
"87,650",
@@ -1101,6 +1190,7 @@
assertFormatDescending(
"Increment",
"precision-increment/0.5",
+ "precision-increment/0.5",
NumberFormatter.with().precision(Precision.increment(BigDecimal.valueOf(0.5))),
ULocale.ENGLISH,
"87,650.0",
@@ -1116,6 +1206,7 @@
assertFormatDescending(
"Increment with Min Fraction",
"precision-increment/0.50",
+ "precision-increment/0.50",
NumberFormatter.with().precision(Precision.increment(new BigDecimal("0.50"))),
ULocale.ENGLISH,
"87,650.00",
@@ -1131,6 +1222,7 @@
assertFormatDescending(
"Strange Increment",
"precision-increment/3.140",
+ "precision-increment/3.140",
NumberFormatter.with().precision(Precision.increment(new BigDecimal("3.140"))),
ULocale.ENGLISH,
"87,649.960",
@@ -1146,6 +1238,7 @@
assertFormatDescending(
"Increment Resolving to Power of 10",
"precision-increment/0.010",
+ "precision-increment/0.010",
NumberFormatter.with().precision(Precision.increment(new BigDecimal("0.010"))),
ULocale.ENGLISH,
"87,650.000",
@@ -1161,6 +1254,7 @@
assertFormatDescending(
"Currency Standard",
"currency/CZK precision-currency-standard",
+ "currency/CZK precision-currency-standard",
NumberFormatter.with().precision(Precision.currency(CurrencyUsage.STANDARD)).unit(CZK),
ULocale.ENGLISH,
"CZK 87,650.00",
@@ -1176,6 +1270,7 @@
assertFormatDescending(
"Currency Cash",
"currency/CZK precision-currency-cash",
+ "currency/CZK precision-currency-cash",
NumberFormatter.with().precision(Precision.currency(CurrencyUsage.CASH)).unit(CZK),
ULocale.ENGLISH,
"CZK 87,650",
@@ -1191,6 +1286,7 @@
assertFormatDescending(
"Currency Cash with Nickel Rounding",
"currency/CAD precision-currency-cash",
+ "currency/CAD precision-currency-cash",
NumberFormatter.with().precision(Precision.currency(CurrencyUsage.CASH)).unit(CAD),
ULocale.ENGLISH,
"CA$87,650.00",
@@ -1206,6 +1302,7 @@
assertFormatDescending(
"Currency not in top-level fluent chain",
"precision-integer", // calling .withCurrency() applies currency rounding rules immediately
+ ".",
NumberFormatter.with().precision(Precision.currency(CurrencyUsage.CASH).withCurrency(CZK)),
ULocale.ENGLISH,
"87,650",
@@ -1222,6 +1319,7 @@
assertFormatDescending(
"Rounding Mode CEILING",
"precision-integer rounding-mode-ceiling",
+ ". rounding-mode-ceiling",
NumberFormatter.with().precision(Precision.integer()).roundingMode(RoundingMode.CEILING),
ULocale.ENGLISH,
"87,650",
@@ -1240,6 +1338,7 @@
assertFormatDescendingBig(
"Western Grouping",
"group-auto",
+ "",
NumberFormatter.with().grouping(GroupingStrategy.AUTO),
ULocale.ENGLISH,
"87,650,000",
@@ -1255,6 +1354,7 @@
assertFormatDescendingBig(
"Indic Grouping",
"group-auto",
+ "",
NumberFormatter.with().grouping(GroupingStrategy.AUTO),
new ULocale("en-IN"),
"8,76,50,000",
@@ -1270,6 +1370,7 @@
assertFormatDescendingBig(
"Western Grouping, Min 2",
"group-min2",
+ ",?",
NumberFormatter.with().grouping(GroupingStrategy.MIN2),
ULocale.ENGLISH,
"87,650,000",
@@ -1285,6 +1386,7 @@
assertFormatDescendingBig(
"Indic Grouping, Min 2",
"group-min2",
+ ",?",
NumberFormatter.with().grouping(GroupingStrategy.MIN2),
new ULocale("en-IN"),
"8,76,50,000",
@@ -1300,6 +1402,7 @@
assertFormatDescendingBig(
"No Grouping",
"group-off",
+ ",_",
NumberFormatter.with().grouping(GroupingStrategy.OFF),
new ULocale("en-IN"),
"87650000",
@@ -1315,6 +1418,7 @@
assertFormatDescendingBig(
"Indic locale with THOUSANDS grouping",
"group-thousands",
+ "group-thousands",
NumberFormatter.with().grouping(GroupingStrategy.THOUSANDS),
new ULocale("en-IN"),
"87,650,000",
@@ -1333,6 +1437,7 @@
assertFormatDescendingBig(
"Polish Grouping",
"group-auto",
+ "",
NumberFormatter.with().grouping(GroupingStrategy.AUTO),
new ULocale("pl"),
"87 650 000",
@@ -1348,6 +1453,7 @@
assertFormatDescendingBig(
"Polish Grouping, Min 2",
"group-min2",
+ ",?",
NumberFormatter.with().grouping(GroupingStrategy.MIN2),
new ULocale("pl"),
"87 650 000",
@@ -1363,6 +1469,7 @@
assertFormatDescendingBig(
"Polish Grouping, Always",
"group-on-aligned",
+ ",!",
NumberFormatter.with().grouping(GroupingStrategy.ON_ALIGNED),
new ULocale("pl"),
"87 650 000",
@@ -1380,6 +1487,7 @@
assertFormatDescendingBig(
"Bulgarian Currency Grouping",
"currency/USD group-auto",
+ "currency/USD",
NumberFormatter.with().grouping(GroupingStrategy.AUTO).unit(USD),
new ULocale("bg"),
"87650000,00 щ.д.",
@@ -1395,6 +1503,7 @@
assertFormatDescendingBig(
"Bulgarian Currency Grouping, Always",
"currency/USD group-on-aligned",
+ "currency/USD ,!",
NumberFormatter.with().grouping(GroupingStrategy.ON_ALIGNED).unit(USD),
new ULocale("bg"),
"87 650 000,00 щ.д.",
@@ -1412,6 +1521,7 @@
assertFormatDescendingBig(
"Custom Grouping via Internal API",
null,
+ null,
NumberFormatter.with().macros(macros),
ULocale.ENGLISH,
"8,7,6,5,0000",
@@ -1430,6 +1540,7 @@
assertFormatDescending(
"Padding",
null,
+ null,
NumberFormatter.with().padding(Padder.none()),
ULocale.ENGLISH,
"87,650",
@@ -1445,6 +1556,7 @@
assertFormatDescending(
"Padding",
null,
+ null,
NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.AFTER_PREFIX)),
ULocale.ENGLISH,
"**87,650",
@@ -1460,6 +1572,7 @@
assertFormatDescending(
"Padding with code points",
null,
+ null,
NumberFormatter.with().padding(Padder.codePoints(0x101E4, 8, PadPosition.AFTER_PREFIX)),
ULocale.ENGLISH,
"𐇤𐇤87,650",
@@ -1475,6 +1588,7 @@
assertFormatDescending(
"Padding with wide digits",
null,
+ null,
NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.AFTER_PREFIX))
.symbols(NumberingSystem.getInstanceByName("mathsanb")),
ULocale.ENGLISH,
@@ -1491,6 +1605,7 @@
assertFormatDescending(
"Padding with currency spacing",
null,
+ null,
NumberFormatter.with().padding(Padder.codePoints('*', 10, PadPosition.AFTER_PREFIX)).unit(GBP)
.unitWidth(UnitWidth.ISO_CODE),
ULocale.ENGLISH,
@@ -1507,6 +1622,7 @@
assertFormatSingle(
"Pad Before Prefix",
null,
+ null,
NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.BEFORE_PREFIX)),
ULocale.ENGLISH,
-88.88,
@@ -1515,6 +1631,7 @@
assertFormatSingle(
"Pad After Prefix",
null,
+ null,
NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.AFTER_PREFIX)),
ULocale.ENGLISH,
-88.88,
@@ -1523,6 +1640,7 @@
assertFormatSingle(
"Pad Before Suffix",
null,
+ null,
NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.BEFORE_SUFFIX))
.unit(NoUnit.PERCENT),
ULocale.ENGLISH,
@@ -1532,6 +1650,7 @@
assertFormatSingle(
"Pad After Suffix",
null,
+ null,
NumberFormatter.with().padding(Padder.codePoints('*', 8, PadPosition.AFTER_SUFFIX))
.unit(NoUnit.PERCENT),
ULocale.ENGLISH,
@@ -1541,6 +1660,7 @@
assertFormatSingle(
"Currency Spacing with Zero Digit Padding Broken",
null,
+ null,
NumberFormatter.with().padding(Padder.codePoints('0', 12, PadPosition.AFTER_PREFIX)).unit(GBP)
.unitWidth(UnitWidth.ISO_CODE),
ULocale.ENGLISH,
@@ -1553,6 +1673,7 @@
assertFormatDescending(
"Integer Width Default",
"integer-width/+0",
+ "0",
NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(1)),
ULocale.ENGLISH,
"87,650",
@@ -1568,6 +1689,7 @@
assertFormatDescending(
"Integer Width Zero Fill 0",
"integer-width/+",
+ "integer-width/+",
NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(0)),
ULocale.ENGLISH,
"87,650",
@@ -1583,6 +1705,7 @@
assertFormatDescending(
"Integer Width Zero Fill 3",
"integer-width/+000",
+ "000",
NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(3)),
ULocale.ENGLISH,
"87,650",
@@ -1598,6 +1721,7 @@
assertFormatDescending(
"Integer Width Max 3",
"integer-width/##0",
+ "integer-width/##0",
NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(1).truncateAt(3)),
ULocale.ENGLISH,
"650",
@@ -1613,6 +1737,7 @@
assertFormatDescending(
"Integer Width Fixed 2",
"integer-width/00",
+ "integer-width/00",
NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(2).truncateAt(2)),
ULocale.ENGLISH,
"50",
@@ -1628,6 +1753,7 @@
assertFormatDescending(
"Integer Width Compact",
"compact-short integer-width/000",
+ "K integer-width/000",
NumberFormatter.with()
.notation(Notation.compactShort())
.integerWidth(IntegerWidth.zeroFillTo(3).truncateAt(3)),
@@ -1645,6 +1771,7 @@
assertFormatDescending(
"Integer Width Scientific",
"scientific integer-width/000",
+ "E0 integer-width/000",
NumberFormatter.with()
.notation(Notation.scientific())
.integerWidth(IntegerWidth.zeroFillTo(3).truncateAt(3)),
@@ -1662,6 +1789,7 @@
assertFormatDescending(
"Integer Width Engineering",
"engineering integer-width/000",
+ "EE0 integer-width/000",
NumberFormatter.with()
.notation(Notation.engineering())
.integerWidth(IntegerWidth.zeroFillTo(3).truncateAt(3)),
@@ -1679,6 +1807,7 @@
assertFormatSingle(
"Integer Width Remove All A",
"integer-width/00",
+ "integer-width/00",
NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(2).truncateAt(2)),
ULocale.ENGLISH,
2500,
@@ -1687,6 +1816,7 @@
assertFormatSingle(
"Integer Width Remove All B",
"integer-width/00",
+ "integer-width/00",
NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(2).truncateAt(2)),
ULocale.ENGLISH,
25000,
@@ -1695,6 +1825,7 @@
assertFormatSingle(
"Integer Width Remove All B, Bytes Mode",
"integer-width/00",
+ "integer-width/00",
NumberFormatter.with().integerWidth(IntegerWidth.zeroFillTo(2).truncateAt(2)),
ULocale.ENGLISH,
// Note: this double produces all 17 significant digits
@@ -1707,6 +1838,7 @@
assertFormatDescending(
"French Symbols with Japanese Data 1",
null,
+ null,
NumberFormatter.with().symbols(DecimalFormatSymbols.getInstance(ULocale.FRENCH)),
ULocale.JAPAN,
"87\u202F650",
@@ -1722,6 +1854,7 @@
assertFormatSingle(
"French Symbols with Japanese Data 2",
null,
+ null,
NumberFormatter.with().notation(Notation.compactShort())
.symbols(DecimalFormatSymbols.getInstance(ULocale.FRENCH)),
ULocale.JAPAN,
@@ -1731,6 +1864,7 @@
assertFormatDescending(
"Latin Numbering System with Arabic Data",
"currency/USD latin",
+ "currency/USD latin",
NumberFormatter.with().symbols(NumberingSystem.LATIN).unit(USD),
new ULocale("ar"),
"US$ 87,650.00",
@@ -1746,6 +1880,7 @@
assertFormatDescending(
"Math Numbering System with French Data",
"numbering-system/mathsanb",
+ "numbering-system/mathsanb",
NumberFormatter.with().symbols(NumberingSystem.getInstanceByName("mathsanb")),
ULocale.FRENCH,
"𝟴𝟳\u202f𝟲𝟱𝟬",
@@ -1761,6 +1896,7 @@
assertFormatSingle(
"Swiss Symbols (used in documentation)",
null,
+ null,
NumberFormatter.with().symbols(DecimalFormatSymbols.getInstance(new ULocale("de-CH"))),
ULocale.ENGLISH,
12345.67,
@@ -1769,6 +1905,7 @@
assertFormatSingle(
"Myanmar Symbols (used in documentation)",
null,
+ null,
NumberFormatter.with().symbols(DecimalFormatSymbols.getInstance(new ULocale("my_MY"))),
ULocale.ENGLISH,
12345.67,
@@ -1779,6 +1916,7 @@
assertFormatSingle(
"Currency symbol should precede number in ar with NS latn",
"currency/USD latin",
+ "currency/USD latin",
NumberFormatter.with().symbols(NumberingSystem.LATIN).unit(USD),
new ULocale("ar"),
12345.67,
@@ -1787,6 +1925,7 @@
assertFormatSingle(
"Currency symbol should precede number in ar@numbers=latn",
"currency/USD",
+ "currency/USD",
NumberFormatter.with().unit(USD),
new ULocale("ar@numbers=latn"),
12345.67,
@@ -1795,6 +1934,7 @@
assertFormatSingle(
"Currency symbol should follow number in ar-EG with NS arab",
"currency/USD",
+ "currency/USD",
NumberFormatter.with().unit(USD),
new ULocale("ar-EG"),
12345.67,
@@ -1803,6 +1943,7 @@
assertFormatSingle(
"Currency symbol should follow number in ar@numbers=arab",
"currency/USD",
+ "currency/USD",
NumberFormatter.with().unit(USD),
new ULocale("ar@numbers=arab"),
12345.67,
@@ -1811,6 +1952,7 @@
assertFormatSingle(
"NumberingSystem in API should win over @numbers keyword",
"currency/USD latin",
+ "currency/USD latin",
NumberFormatter.with().symbols(NumberingSystem.LATIN).unit(USD),
new ULocale("ar@numbers=arab"),
12345.67,
@@ -1830,6 +1972,7 @@
assertFormatSingle(
"Symbols object should be copied",
null,
+ null,
f,
ULocale.ENGLISH,
12345.67,
@@ -1838,6 +1981,7 @@
assertFormatSingle(
"The last symbols setter wins",
"latin",
+ "latin",
NumberFormatter.with().symbols(symbols).symbols(NumberingSystem.LATIN),
ULocale.ENGLISH,
12345.67,
@@ -1846,6 +1990,7 @@
assertFormatSingle(
"The last symbols setter wins",
null,
+ null,
NumberFormatter.with().symbols(NumberingSystem.LATIN).symbols(symbols),
ULocale.ENGLISH,
12345.67,
@@ -1861,6 +2006,7 @@
assertFormatSingle(
"Custom Short Currency Symbol",
"$XXX",
+ "$XXX",
NumberFormatter.with().unit(Currency.getInstance("XXX")).symbols(dfs),
ULocale.ENGLISH,
12.3,
@@ -1872,6 +2018,7 @@
assertFormatSingle(
"Sign Auto Positive",
"sign-auto",
+ "",
NumberFormatter.with().sign(SignDisplay.AUTO),
ULocale.ENGLISH,
444444,
@@ -1880,6 +2027,7 @@
assertFormatSingle(
"Sign Auto Negative",
"sign-auto",
+ "",
NumberFormatter.with().sign(SignDisplay.AUTO),
ULocale.ENGLISH,
-444444,
@@ -1888,6 +2036,7 @@
assertFormatSingle(
"Sign Auto Zero",
"sign-auto",
+ "",
NumberFormatter.with().sign(SignDisplay.AUTO),
ULocale.ENGLISH,
0,
@@ -1896,6 +2045,7 @@
assertFormatSingle(
"Sign Always Positive",
"sign-always",
+ "+!",
NumberFormatter.with().sign(SignDisplay.ALWAYS),
ULocale.ENGLISH,
444444,
@@ -1904,6 +2054,7 @@
assertFormatSingle(
"Sign Always Negative",
"sign-always",
+ "+!",
NumberFormatter.with().sign(SignDisplay.ALWAYS),
ULocale.ENGLISH,
-444444,
@@ -1912,6 +2063,7 @@
assertFormatSingle(
"Sign Always Zero",
"sign-always",
+ "+!",
NumberFormatter.with().sign(SignDisplay.ALWAYS),
ULocale.ENGLISH,
0,
@@ -1920,6 +2072,7 @@
assertFormatSingle(
"Sign Never Positive",
"sign-never",
+ "+_",
NumberFormatter.with().sign(SignDisplay.NEVER),
ULocale.ENGLISH,
444444,
@@ -1928,6 +2081,7 @@
assertFormatSingle(
"Sign Never Negative",
"sign-never",
+ "+_",
NumberFormatter.with().sign(SignDisplay.NEVER),
ULocale.ENGLISH,
-444444,
@@ -1936,6 +2090,7 @@
assertFormatSingle(
"Sign Never Zero",
"sign-never",
+ "+_",
NumberFormatter.with().sign(SignDisplay.NEVER),
ULocale.ENGLISH,
0,
@@ -1944,6 +2099,7 @@
assertFormatSingle(
"Sign Accounting Positive",
"currency/USD sign-accounting",
+ "currency/USD ()",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD),
ULocale.ENGLISH,
444444,
@@ -1952,6 +2108,7 @@
assertFormatSingle(
"Sign Accounting Negative",
"currency/USD sign-accounting",
+ "currency/USD ()",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD),
ULocale.ENGLISH,
-444444,
@@ -1960,6 +2117,7 @@
assertFormatSingle(
"Sign Accounting Zero",
"currency/USD sign-accounting",
+ "currency/USD ()",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD),
ULocale.ENGLISH,
0,
@@ -1968,6 +2126,7 @@
assertFormatSingle(
"Sign Accounting-Always Positive",
"currency/USD sign-accounting-always",
+ "currency/USD ()!",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING_ALWAYS).unit(USD),
ULocale.ENGLISH,
444444,
@@ -1976,6 +2135,7 @@
assertFormatSingle(
"Sign Accounting-Always Negative",
"currency/USD sign-accounting-always",
+ "currency/USD ()!",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING_ALWAYS).unit(USD),
ULocale.ENGLISH,
-444444,
@@ -1984,6 +2144,7 @@
assertFormatSingle(
"Sign Accounting-Always Zero",
"currency/USD sign-accounting-always",
+ "currency/USD ()!",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING_ALWAYS).unit(USD),
ULocale.ENGLISH,
0,
@@ -1992,6 +2153,7 @@
assertFormatSingle(
"Sign Except-Zero Positive",
"sign-except-zero",
+ "+?",
NumberFormatter.with().sign(SignDisplay.EXCEPT_ZERO),
ULocale.ENGLISH,
444444,
@@ -2000,6 +2162,7 @@
assertFormatSingle(
"Sign Except-Zero Negative",
"sign-except-zero",
+ "+?",
NumberFormatter.with().sign(SignDisplay.EXCEPT_ZERO),
ULocale.ENGLISH,
-444444,
@@ -2008,6 +2171,7 @@
assertFormatSingle(
"Sign Except-Zero Zero",
"sign-except-zero",
+ "+?",
NumberFormatter.with().sign(SignDisplay.EXCEPT_ZERO),
ULocale.ENGLISH,
0,
@@ -2016,6 +2180,7 @@
assertFormatSingle(
"Sign Accounting-Except-Zero Positive",
"currency/USD sign-accounting-except-zero",
+ "currency/USD ()?",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING_EXCEPT_ZERO).unit(USD),
ULocale.ENGLISH,
444444,
@@ -2024,6 +2189,7 @@
assertFormatSingle(
"Sign Accounting-Except-Zero Negative",
"currency/USD sign-accounting-except-zero",
+ "currency/USD ()?",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING_EXCEPT_ZERO).unit(USD),
ULocale.ENGLISH,
-444444,
@@ -2032,6 +2198,7 @@
assertFormatSingle(
"Sign Accounting-Except-Zero Zero",
"currency/USD sign-accounting-except-zero",
+ "currency/USD ()?",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING_EXCEPT_ZERO).unit(USD),
ULocale.ENGLISH,
0,
@@ -2040,6 +2207,7 @@
assertFormatSingle(
"Sign Accounting Negative Hidden",
"currency/USD unit-width-hidden sign-accounting",
+ "currency/USD unit-width-hidden ()",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD).unitWidth(UnitWidth.HIDDEN),
ULocale.ENGLISH,
-444444,
@@ -2048,6 +2216,7 @@
assertFormatSingle(
"Sign Accounting Negative Narrow",
"currency/USD unit-width-narrow sign-accounting",
+ "currency/USD unit-width-narrow ()",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD).unitWidth(UnitWidth.NARROW),
ULocale.CANADA,
-444444,
@@ -2056,6 +2225,7 @@
assertFormatSingle(
"Sign Accounting Negative Short",
"currency/USD sign-accounting",
+ "currency/USD ()",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD).unitWidth(UnitWidth.SHORT),
ULocale.CANADA,
-444444,
@@ -2064,6 +2234,7 @@
assertFormatSingle(
"Sign Accounting Negative Iso Code",
"currency/USD unit-width-iso-code sign-accounting",
+ "currency/USD unit-width-iso-code ()",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD).unitWidth(UnitWidth.ISO_CODE),
ULocale.CANADA,
-444444,
@@ -2074,6 +2245,7 @@
assertFormatSingle(
"Sign Accounting Negative Full Name",
"currency/USD unit-width-full-name sign-accounting",
+ "currency/USD unit-width-full-name ()",
NumberFormatter.with().sign(SignDisplay.ACCOUNTING).unit(USD).unitWidth(UnitWidth.FULL_NAME),
ULocale.CANADA,
-444444,
@@ -2154,6 +2326,7 @@
assertFormatDescending(
"Decimal Default",
"decimal-auto",
+ "",
NumberFormatter.with().decimal(DecimalSeparatorDisplay.AUTO),
ULocale.ENGLISH,
"87,650",
@@ -2169,6 +2342,7 @@
assertFormatDescending(
"Decimal Always Shown",
"decimal-always",
+ "decimal-always",
NumberFormatter.with().decimal(DecimalSeparatorDisplay.ALWAYS),
ULocale.ENGLISH,
"87,650.",
@@ -2187,6 +2361,7 @@
assertFormatDescending(
"Multiplier None",
"scale/1",
+ "",
NumberFormatter.with().scale(Scale.none()),
ULocale.ENGLISH,
"87,650",
@@ -2202,6 +2377,7 @@
assertFormatDescending(
"Multiplier Power of Ten",
"scale/1000000",
+ "scale/1000000",
NumberFormatter.with().scale(Scale.powerOfTen(6)),
ULocale.ENGLISH,
"87,650,000,000",
@@ -2217,6 +2393,7 @@
assertFormatDescending(
"Multiplier Arbitrary Double",
"scale/5.2",
+ "scale/5.2",
NumberFormatter.with().scale(Scale.byDouble(5.2)),
ULocale.ENGLISH,
"455,780",
@@ -2232,6 +2409,7 @@
assertFormatDescending(
"Multiplier Arbitrary BigDecimal",
"scale/5.2",
+ "scale/5.2",
NumberFormatter.with().scale(Scale.byBigDecimal(new BigDecimal("5.2"))),
ULocale.ENGLISH,
"455,780",
@@ -2247,6 +2425,7 @@
assertFormatDescending(
"Multiplier Arbitrary Double And Power Of Ten",
"scale/5200",
+ "scale/5200",
NumberFormatter.with().scale(Scale.byDoubleAndPowerOfTen(5.2, 3)),
ULocale.ENGLISH,
"455,780,000",
@@ -2262,6 +2441,7 @@
assertFormatDescending(
"Multiplier Zero",
"scale/0",
+ "scale/0",
NumberFormatter.with().scale(Scale.byDouble(0)),
ULocale.ENGLISH,
"0",
@@ -2277,6 +2457,7 @@
assertFormatSingle(
"Multiplier Skeleton Scientific Notation and Percent",
"percent scale/1E2",
+ "%x100",
NumberFormatter.with().unit(NoUnit.PERCENT).scale(Scale.powerOfTen(2)),
ULocale.ENGLISH,
0.5,
@@ -2285,6 +2466,7 @@
assertFormatSingle(
"Negative Multiplier",
"scale/-5.2",
+ "scale/-5.2",
NumberFormatter.with().scale(Scale.byDouble(-5.2)),
ULocale.ENGLISH,
2,
@@ -2293,6 +2475,7 @@
assertFormatSingle(
"Negative One Multiplier",
"scale/-1",
+ "scale/-1",
NumberFormatter.with().scale(Scale.byDouble(-1)),
ULocale.ENGLISH,
444444,
@@ -2301,6 +2484,7 @@
assertFormatSingle(
"Two-Type Multiplier with Overlap",
"scale/10000",
+ "scale/10000",
NumberFormatter.with().scale(Scale.byDoubleAndPowerOfTen(100, 2)),
ULocale.ENGLISH,
2,
@@ -2346,6 +2530,7 @@
FormattedNumber fmtd = assertFormatSingle(
message,
"",
+ "",
NumberFormatter.with(),
ULocale.ENGLISH,
-9876543210.12,
@@ -2399,6 +2584,7 @@
FormattedNumber result = assertFormatSingle(
message,
"measure-unit/temperature-fahrenheit",
+ "unit/fahrenheit",
NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT),
ULocale.ENGLISH,
68,
@@ -2418,6 +2604,7 @@
FormattedNumber result = assertFormatSingle(
message,
"measure-unit/temperature-fahrenheit per-measure-unit/duration-day",
+ "unit/fahrenheit-per-day",
NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT).perUnit(MeasureUnit.DAY),
ULocale.ENGLISH,
68,
@@ -2437,6 +2624,7 @@
FormattedNumber result = assertFormatSingle(
message,
"measure-unit/length-meter unit-width-full-name",
+ "unit/meter unit-width-full-name",
NumberFormatter.with().unit(MeasureUnit.METER).unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
68,
@@ -2457,6 +2645,7 @@
FormattedNumber result = assertFormatSingle(
message,
"measure-unit/length-meter per-measure-unit/duration-second unit-width-full-name",
+ "~unit/meter-per-second unit-width-full-name", // does not round-trip to the full skeleton above
NumberFormatter.with().unit(MeasureUnit.METER).perUnit(MeasureUnit.SECOND).unitWidth(UnitWidth.FULL_NAME),
new ULocale("ky"), // locale with the interesting data
68,
@@ -2477,6 +2666,7 @@
FormattedNumber result = assertFormatSingle(
message,
"measure-unit/temperature-fahrenheit unit-width-full-name",
+ "unit/fahrenheit unit-width-full-name",
NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT).unitWidth(UnitWidth.FULL_NAME),
new ULocale("vi"), // locale with the interesting data
68,
@@ -2500,6 +2690,7 @@
FormattedNumber result = assertFormatSingle(
message,
"measure-unit/temperature-kelvin",
+ "unit/kelvin",
NumberFormatter.with().unit(MeasureUnit.KELVIN),
new ULocale("fa"), // locale with the interesting data
68,
@@ -2519,6 +2710,7 @@
FormattedNumber result = assertFormatSingle(
message,
"compact-short",
+ "K",
NumberFormatter.with().notation(Notation.compactShort()),
ULocale.US,
65000,
@@ -2538,6 +2730,7 @@
FormattedNumber result = assertFormatSingle(
message,
"compact-long",
+ "KK",
NumberFormatter.with().notation(Notation.compactLong()),
ULocale.US,
65000,
@@ -2557,6 +2750,7 @@
FormattedNumber result = assertFormatSingle(
message,
"compact-long",
+ "KK",
NumberFormatter.with().notation(Notation.compactLong()),
new ULocale("fil"), // locale with interesting data
6000,
@@ -2576,6 +2770,7 @@
FormattedNumber result = assertFormatSingle(
message,
"compact-long",
+ "KK",
NumberFormatter.with().notation(Notation.compactLong()),
new ULocale("he"), // locale with interesting data
6000,
@@ -2595,6 +2790,7 @@
FormattedNumber result = assertFormatSingle(
message,
"compact-short currency/USD",
+ "K currency/USD",
NumberFormatter.with().notation(Notation.compactShort()).unit(USD),
new ULocale("sr_Latn"), // locale with interesting data
65000,
@@ -2615,6 +2811,7 @@
FormattedNumber result = assertFormatSingle(
message,
"currency/USD unit-width-full-name",
+ "currency/USD unit-width-full-name",
NumberFormatter.with().unit(USD)
.unitWidth(UnitWidth.FULL_NAME),
ULocale.ENGLISH,
@@ -2638,6 +2835,7 @@
FormattedNumber result = assertFormatSingle(
message,
"compact-long measure-unit/length-meter unit-width-full-name",
+ "KK unit/meter unit-width-full-name",
NumberFormatter.with().notation(Notation.compactLong())
.unit(MeasureUnit.METER)
.unitWidth(UnitWidth.FULL_NAME),
@@ -2704,6 +2902,7 @@
assertFormatSingle(
"Plural 1",
"currency/USD precision-integer unit-width-full-name",
+ "currency/USD . unit-width-full-name",
NumberFormatter.with().unit(USD).unitWidth(UnitWidth.FULL_NAME).precision(Precision.fixedFraction(0)),
ULocale.ENGLISH,
1,
@@ -2712,6 +2911,7 @@
assertFormatSingle(
"Plural 1.00",
"currency/USD .00 unit-width-full-name",
+ "currency/USD .00 unit-width-full-name",
NumberFormatter.with().unit(USD).unitWidth(UnitWidth.FULL_NAME).precision(Precision.fixedFraction(2)),
ULocale.ENGLISH,
1,
@@ -2835,26 +3035,29 @@
static void assertFormatDescending(
String message,
String skeleton,
+ String conciseSkeleton,
UnlocalizedNumberFormatter f,
ULocale locale,
String... expected) {
final double[] inputs = new double[] { 87650, 8765, 876.5, 87.65, 8.765, 0.8765, 0.08765, 0.008765, 0 };
- assertFormatDescending(message, skeleton, f, locale, inputs, expected);
+ assertFormatDescending(message, skeleton, conciseSkeleton, f, locale, inputs, expected);
}
static void assertFormatDescendingBig(
String message,
String skeleton,
+ String conciseSkeleton,
UnlocalizedNumberFormatter f,
ULocale locale,
String... expected) {
final double[] inputs = new double[] { 87650000, 8765000, 876500, 87650, 8765, 876.5, 87.65, 8.765, 0 };
- assertFormatDescending(message, skeleton, f, locale, inputs, expected);
+ assertFormatDescending(message, skeleton, conciseSkeleton, f, locale, inputs, expected);
}
static void assertFormatDescending(
String message,
String skeleton,
+ String conciseSkeleton,
UnlocalizedNumberFormatter f,
ULocale locale,
double[] inputs,
@@ -2880,6 +3083,22 @@
String actual3 = l3.format(d).toString();
assertEquals(message + ": Skeleton Path: " + d, expected[i], actual3);
}
+ // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
+ // If the concise skeleton starts with '~', disable the round-trip check.
+ boolean shouldRoundTrip = true;
+ if (conciseSkeleton.length() > 0 && conciseSkeleton.charAt(0) == '~') {
+ conciseSkeleton = conciseSkeleton.substring(1);
+ shouldRoundTrip = false;
+ }
+ LocalizedNumberFormatter l4 = NumberFormatter.forSkeleton(conciseSkeleton).locale(locale);
+ if (shouldRoundTrip) {
+ assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton());
+ }
+ for (int i = 0; i < 9; i++) {
+ double d = inputs[i];
+ String actual4 = l4.format(d).toString();
+ assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + d, expected[i], actual4);
+ }
} else {
assertUndefinedSkeleton(f);
}
@@ -2888,6 +3107,7 @@
static FormattedNumber assertFormatSingle(
String message,
String skeleton,
+ String conciseSkeleton,
UnlocalizedNumberFormatter f,
ULocale locale,
Number input,
@@ -2907,6 +3127,19 @@
LocalizedNumberFormatter l3 = NumberFormatter.forSkeleton(normalized).locale(locale);
String actual3 = l3.format(input).toString();
assertEquals(message + ": Skeleton Path: " + input, expected, actual3);
+ // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
+ // If the concise skeleton starts with '~', disable the round-trip check.
+ boolean shouldRoundTrip = true;
+ if (conciseSkeleton.length() > 0 && conciseSkeleton.charAt(0) == '~') {
+ conciseSkeleton = conciseSkeleton.substring(1);
+ shouldRoundTrip = false;
+ }
+ LocalizedNumberFormatter l4 = NumberFormatter.forSkeleton(conciseSkeleton).locale(locale);
+ if (shouldRoundTrip) {
+ assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton());
+ }
+ String actual4 = l4.format(input).toString();
+ assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + input, expected, actual4);
} else {
assertUndefinedSkeleton(f);
}
@@ -2916,6 +3149,7 @@
static void assertFormatSingleMeasure(
String message,
String skeleton,
+ String conciseSkeleton,
UnlocalizedNumberFormatter f,
ULocale locale,
Measure input,
@@ -2934,6 +3168,19 @@
LocalizedNumberFormatter l3 = NumberFormatter.forSkeleton(normalized).locale(locale);
String actual3 = l3.format(input).toString();
assertEquals(message + ": Skeleton Path: " + input, expected, actual3);
+ // Concise skeletons should have same output, and usually round-trip to the normalized skeleton.
+ // If the concise skeleton starts with '~', disable the round-trip check.
+ boolean shouldRoundTrip = true;
+ if (conciseSkeleton.length() > 0 && conciseSkeleton.charAt(0) == '~') {
+ conciseSkeleton = conciseSkeleton.substring(1);
+ shouldRoundTrip = false;
+ }
+ LocalizedNumberFormatter l4 = NumberFormatter.forSkeleton(conciseSkeleton).locale(locale);
+ if (shouldRoundTrip) {
+ assertEquals(message + ": Concise Skeleton:", normalized, l4.toSkeleton());
+ }
+ String actual4 = l4.format(input).toString();
+ assertEquals(message + ": Concise Skeleton Path: '" + normalized + "': " + input, expected, actual4);
} else {
assertUndefinedSkeleton(f);
}
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java
index 8d5f83b..a2c9553 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberSkeletonTest.java
@@ -57,6 +57,7 @@
"measure-unit/length-meter",
"measure-unit/area-square-meter",
"measure-unit/energy-joule per-measure-unit/length-meter",
+ "unit/square-meter-per-square-meter",
"currency/XXX",
"currency/ZZZ",
"currency/usd",
@@ -92,7 +93,20 @@
"numbering-system/latn",
"precision-integer/@##",
"precision-integer rounding-mode-ceiling",
- "precision-currency-cash rounding-mode-ceiling" };
+ "precision-currency-cash rounding-mode-ceiling",
+ "0",
+ "00",
+ "000",
+ "E0",
+ "E00",
+ "E000",
+ "EE0",
+ "EE00",
+ "EE+?0",
+ "EE+?00",
+ "EE+!0",
+ "EE+!00",
+ };
for (String cas : cases) {
try {
@@ -137,7 +151,20 @@
"integer-width/+0#",
"integer-width/+#",
"integer-width/+#0",
- "scientific/foo" };
+ "scientific/foo",
+ "E",
+ "E1",
+ "E+",
+ "E+?",
+ "E+!",
+ "E+0",
+ "EE",
+ "EE+",
+ "EEE",
+ "EEE0",
+ "001",
+ "00+",
+ };
for (String cas : cases) {
try {