| // © 2018 and later: Unicode, Inc. and others. |
| // License & terms of use: http://www.unicode.org/copyright.html |
| package com.ibm.icu.dev.test.number; |
| |
| import static org.junit.Assert.assertEquals; |
| |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.junit.Test; |
| |
| import com.ibm.icu.dev.test.format.FormattedValueTest; |
| import com.ibm.icu.impl.ICUData; |
| import com.ibm.icu.impl.ICUResourceBundle; |
| import com.ibm.icu.impl.UResource; |
| import com.ibm.icu.number.FormattedNumberRange; |
| import com.ibm.icu.number.LocalizedNumberFormatter; |
| import com.ibm.icu.number.LocalizedNumberRangeFormatter; |
| import com.ibm.icu.number.Notation; |
| import com.ibm.icu.number.NumberFormatter; |
| import com.ibm.icu.number.NumberFormatter.UnitWidth; |
| import com.ibm.icu.number.NumberRangeFormatter; |
| import com.ibm.icu.number.NumberRangeFormatter.RangeCollapse; |
| import com.ibm.icu.number.NumberRangeFormatter.RangeIdentityFallback; |
| import com.ibm.icu.number.Precision; |
| import com.ibm.icu.number.UnlocalizedNumberFormatter; |
| import com.ibm.icu.number.UnlocalizedNumberRangeFormatter; |
| import com.ibm.icu.text.NumberFormat; |
| import com.ibm.icu.text.NumberingSystem; |
| import com.ibm.icu.util.Currency; |
| import com.ibm.icu.util.MeasureUnit; |
| import com.ibm.icu.util.ULocale; |
| import com.ibm.icu.util.UResourceBundle; |
| |
| /** |
| * @author sffc |
| * |
| */ |
| public class NumberRangeFormatterTest { |
| |
| private static final Currency USD = Currency.getInstance("USD"); |
| private static final Currency GBP = Currency.getInstance("GBP"); |
| private static final Currency PTE = Currency.getInstance("PTE"); |
| |
| @Test |
| public void testSanity() { |
| LocalizedNumberRangeFormatter lnrf1 = NumberRangeFormatter.withLocale(ULocale.US); |
| LocalizedNumberRangeFormatter lnrf2 = NumberRangeFormatter.with().locale(ULocale.US); |
| LocalizedNumberRangeFormatter lnrf3 = NumberRangeFormatter.withLocale(Locale.US); |
| LocalizedNumberRangeFormatter lnrf4 = NumberRangeFormatter.with().locale(Locale.US); |
| assertEquals("Formatters should be equal 1", lnrf1, lnrf2); |
| assertEquals("Formatters should be equal 2", lnrf2, lnrf3); |
| assertEquals("Formatters should be equal 3", lnrf3, lnrf4); |
| assertEquals("Formatters should have same behavior 1", lnrf1.formatRange(4, 6), lnrf2.formatRange(4, 6)); |
| assertEquals("Formatters should have same behavior 2", lnrf2.formatRange(4, 6), lnrf3.formatRange(4, 6)); |
| assertEquals("Formatters should have same behavior 3", lnrf3.formatRange(4, 6), lnrf4.formatRange(4, 6)); |
| } |
| |
| @Test |
| public void testBasic() { |
| assertFormatRange( |
| "Basic", |
| NumberRangeFormatter.with(), |
| new ULocale("en-us"), |
| "1–5", |
| "~5", |
| "~5", |
| "0–3", |
| "~0", |
| "3–3,000", |
| "3,000–5,000", |
| "4,999–5,001", |
| "~5,000", |
| "5,000–5,000,000"); |
| |
| assertFormatRange( |
| "Basic with units", |
| NumberRangeFormatter.with() |
| .numberFormatterBoth(NumberFormatter.with().unit(MeasureUnit.METER)), |
| new ULocale("en-us"), |
| "1–5 m", |
| "~5 m", |
| "~5 m", |
| "0–3 m", |
| "~0 m", |
| "3–3,000 m", |
| "3,000–5,000 m", |
| "4,999–5,001 m", |
| "~5,000 m", |
| "5,000–5,000,000 m"); |
| |
| assertFormatRange( |
| "Basic with different units", |
| NumberRangeFormatter.with() |
| .numberFormatterFirst(NumberFormatter.with().unit(MeasureUnit.METER)) |
| .numberFormatterSecond(NumberFormatter.with().unit(MeasureUnit.KILOMETER)), |
| new ULocale("en-us"), |
| "1 m – 5 km", |
| "5 m – 5 km", |
| "5 m – 5 km", |
| "0 m – 3 km", |
| "0 m – 0 km", |
| "3 m – 3,000 km", |
| "3,000 m – 5,000 km", |
| "4,999 m – 5,001 km", |
| "5,000 m – 5,000 km", |
| "5,000 m – 5,000,000 km"); |
| |
| assertFormatRange( |
| "Basic long unit", |
| NumberRangeFormatter.with() |
| .numberFormatterBoth(NumberFormatter.with().unit(MeasureUnit.METER).unitWidth(UnitWidth.FULL_NAME)), |
| new ULocale("en-us"), |
| "1–5 meters", |
| "~5 meters", |
| "~5 meters", |
| "0–3 meters", |
| "~0 meters", |
| "3–3,000 meters", |
| "3,000–5,000 meters", |
| "4,999–5,001 meters", |
| "~5,000 meters", |
| "5,000–5,000,000 meters"); |
| |
| assertFormatRange( |
| "Non-English locale and unit", |
| NumberRangeFormatter.with() |
| .numberFormatterBoth(NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT).unitWidth(UnitWidth.FULL_NAME)), |
| new ULocale("fr-FR"), |
| "1–5\u00A0degrés Fahrenheit", |
| "≈5\u00A0degrés Fahrenheit", |
| "≈5\u00A0degrés Fahrenheit", |
| "0–3\u00A0degrés Fahrenheit", |
| "≈0\u00A0degré Fahrenheit", |
| "3–3\u202F000\u00A0degrés Fahrenheit", |
| "3\u202F000–5\u202F000\u00A0degrés Fahrenheit", |
| "4\u202F999–5\u202F001\u00A0degrés Fahrenheit", |
| "≈5\u202F000\u00A0degrés Fahrenheit", |
| "5\u202F000–5\u202F000\u202F000\u00A0degrés Fahrenheit"); |
| |
| assertFormatRange( |
| "Locale with custom range separator", |
| NumberRangeFormatter.with(), |
| new ULocale("ja"), |
| "1~5", |
| "約 5", |
| "約 5", |
| "0~3", |
| "約 0", |
| "3~3,000", |
| "3,000~5,000", |
| "4,999~5,001", |
| "約 5,000", |
| "5,000~5,000,000"); |
| |
| assertFormatRange( |
| "Locale that already has spaces around range separator", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.NONE) |
| .numberFormatterBoth(NumberFormatter.with().unit(MeasureUnit.KELVIN)), |
| new ULocale("hr"), |
| "1 K – 5 K", |
| "~5 K", |
| "~5 K", |
| "0 K – 3 K", |
| "~0 K", |
| "3 K – 3.000 K", |
| "3.000 K – 5.000 K", |
| "4.999 K – 5.001 K", |
| "~5.000 K", |
| "5.000 K – 5.000.000 K"); |
| |
| assertFormatRange( |
| "Locale with custom numbering system and no plural ranges data", |
| NumberRangeFormatter.with(), |
| new ULocale("shn@numbers=beng"), |
| // 012459 = ০১৩৪৫৯ |
| "১–৫", |
| "~৫", |
| "~৫", |
| "০–৩", |
| "~০", |
| "৩–৩,০০০", |
| "৩,০০০–৫,০০০", |
| "৪,৯৯৯–৫,০০১", |
| "~৫,০০০", |
| "৫,০০০–৫,০০০,০০০"); |
| |
| assertFormatRange( |
| "Portuguese currency", |
| NumberRangeFormatter.with() |
| .numberFormatterBoth(NumberFormatter.with().unit(PTE)), |
| new ULocale("pt-PT"), |
| "1$00 - 5$00 \u200B", |
| "~5$00 \u200B", |
| "~5$00 \u200B", |
| "0$00 - 3$00 \u200B", |
| "~0$00 \u200B", |
| "3$00 - 3000$00 \u200B", |
| "3000$00 - 5000$00 \u200B", |
| "4999$00 - 5001$00 \u200B", |
| "~5000$00 \u200B", |
| "5000$00 - 5,000,000$00 \u200B"); |
| } |
| |
| @Test |
| public void testCollapse() { |
| assertFormatRange( |
| "Default collapse on currency (default rounding)", |
| NumberRangeFormatter.with() |
| .numberFormatterBoth(NumberFormatter.with().unit(USD)), |
| new ULocale("en-us"), |
| "$1.00 – $5.00", |
| "~$5.00", |
| "~$5.00", |
| "$0.00 – $3.00", |
| "~$0.00", |
| "$3.00 – $3,000.00", |
| "$3,000.00 – $5,000.00", |
| "$4,999.00 – $5,001.00", |
| "~$5,000.00", |
| "$5,000.00 – $5,000,000.00"); |
| |
| assertFormatRange( |
| "Default collapse on currency", |
| NumberRangeFormatter.with() |
| .numberFormatterBoth(NumberFormatter.with().unit(USD).precision(Precision.integer())), |
| new ULocale("en-us"), |
| "$1 – $5", |
| "~$5", |
| "~$5", |
| "$0 – $3", |
| "~$0", |
| "$3 – $3,000", |
| "$3,000 – $5,000", |
| "$4,999 – $5,001", |
| "~$5,000", |
| "$5,000 – $5,000,000"); |
| |
| assertFormatRange( |
| "No collapse on currency", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.NONE) |
| .numberFormatterBoth(NumberFormatter.with().unit(USD).precision(Precision.integer())), |
| new ULocale("en-us"), |
| "$1 – $5", |
| "~$5", |
| "~$5", |
| "$0 – $3", |
| "~$0", |
| "$3 – $3,000", |
| "$3,000 – $5,000", |
| "$4,999 – $5,001", |
| "~$5,000", |
| "$5,000 – $5,000,000"); |
| |
| assertFormatRange( |
| "Unit collapse on currency", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.UNIT) |
| .numberFormatterBoth(NumberFormatter.with().unit(USD).precision(Precision.integer())), |
| new ULocale("en-us"), |
| "$1–5", |
| "~$5", |
| "~$5", |
| "$0–3", |
| "~$0", |
| "$3–3,000", |
| "$3,000–5,000", |
| "$4,999–5,001", |
| "~$5,000", |
| "$5,000–5,000,000"); |
| |
| assertFormatRange( |
| "All collapse on currency", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.ALL) |
| .numberFormatterBoth(NumberFormatter.with().unit(USD).precision(Precision.integer())), |
| new ULocale("en-us"), |
| "$1–5", |
| "~$5", |
| "~$5", |
| "$0–3", |
| "~$0", |
| "$3–3,000", |
| "$3,000–5,000", |
| "$4,999–5,001", |
| "~$5,000", |
| "$5,000–5,000,000"); |
| |
| assertFormatRange( |
| "Default collapse on currency ISO code", |
| NumberRangeFormatter.with() |
| .numberFormatterBoth(NumberFormatter.with() |
| .unit(GBP) |
| .unitWidth(UnitWidth.ISO_CODE) |
| .precision(Precision.integer())), |
| new ULocale("en-us"), |
| "GBP 1–5", |
| "~GBP 5", // TODO: Fix this at some point |
| "~GBP 5", |
| "GBP 0–3", |
| "~GBP 0", |
| "GBP 3–3,000", |
| "GBP 3,000–5,000", |
| "GBP 4,999–5,001", |
| "~GBP 5,000", |
| "GBP 5,000–5,000,000"); |
| |
| assertFormatRange( |
| "No collapse on currency ISO code", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.NONE) |
| .numberFormatterBoth(NumberFormatter.with() |
| .unit(GBP) |
| .unitWidth(UnitWidth.ISO_CODE) |
| .precision(Precision.integer())), |
| new ULocale("en-us"), |
| "GBP 1 – GBP 5", |
| "~GBP 5", // TODO: Fix this at some point |
| "~GBP 5", |
| "GBP 0 – GBP 3", |
| "~GBP 0", |
| "GBP 3 – GBP 3,000", |
| "GBP 3,000 – GBP 5,000", |
| "GBP 4,999 – GBP 5,001", |
| "~GBP 5,000", |
| "GBP 5,000 – GBP 5,000,000"); |
| |
| assertFormatRange( |
| "Unit collapse on currency ISO code", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.UNIT) |
| .numberFormatterBoth(NumberFormatter.with() |
| .unit(GBP) |
| .unitWidth(UnitWidth.ISO_CODE) |
| .precision(Precision.integer())), |
| new ULocale("en-us"), |
| "GBP 1–5", |
| "~GBP 5", // TODO: Fix this at some point |
| "~GBP 5", |
| "GBP 0–3", |
| "~GBP 0", |
| "GBP 3–3,000", |
| "GBP 3,000–5,000", |
| "GBP 4,999–5,001", |
| "~GBP 5,000", |
| "GBP 5,000–5,000,000"); |
| |
| assertFormatRange( |
| "All collapse on currency ISO code", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.ALL) |
| .numberFormatterBoth(NumberFormatter.with() |
| .unit(GBP) |
| .unitWidth(UnitWidth.ISO_CODE) |
| .precision(Precision.integer())), |
| new ULocale("en-us"), |
| "GBP 1–5", |
| "~GBP 5", // TODO: Fix this at some point |
| "~GBP 5", |
| "GBP 0–3", |
| "~GBP 0", |
| "GBP 3–3,000", |
| "GBP 3,000–5,000", |
| "GBP 4,999–5,001", |
| "~GBP 5,000", |
| "GBP 5,000–5,000,000"); |
| |
| // Default collapse on measurement unit is in testBasic() |
| |
| assertFormatRange( |
| "No collapse on measurement unit", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.NONE) |
| .numberFormatterBoth(NumberFormatter.with().unit(MeasureUnit.METER)), |
| new ULocale("en-us"), |
| "1 m – 5 m", |
| "~5 m", |
| "~5 m", |
| "0 m – 3 m", |
| "~0 m", |
| "3 m – 3,000 m", |
| "3,000 m – 5,000 m", |
| "4,999 m – 5,001 m", |
| "~5,000 m", |
| "5,000 m – 5,000,000 m"); |
| |
| assertFormatRange( |
| "Unit collapse on measurement unit", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.UNIT) |
| .numberFormatterBoth(NumberFormatter.with().unit(MeasureUnit.METER)), |
| new ULocale("en-us"), |
| "1–5 m", |
| "~5 m", |
| "~5 m", |
| "0–3 m", |
| "~0 m", |
| "3–3,000 m", |
| "3,000–5,000 m", |
| "4,999–5,001 m", |
| "~5,000 m", |
| "5,000–5,000,000 m"); |
| |
| assertFormatRange( |
| "All collapse on measurement unit", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.ALL) |
| .numberFormatterBoth(NumberFormatter.with().unit(MeasureUnit.METER)), |
| new ULocale("en-us"), |
| "1–5 m", |
| "~5 m", |
| "~5 m", |
| "0–3 m", |
| "~0 m", |
| "3–3,000 m", |
| "3,000–5,000 m", |
| "4,999–5,001 m", |
| "~5,000 m", |
| "5,000–5,000,000 m"); |
| |
| assertFormatRange( |
| "Default collapse, long-form compact notation", |
| NumberRangeFormatter.with() |
| .numberFormatterBoth(NumberFormatter.with().notation(Notation.compactLong())), |
| new ULocale("de-CH"), |
| "1–5", |
| "≈5", |
| "≈5", |
| "0–3", |
| "≈0", |
| "3–3 Tausend", |
| "3–5 Tausend", |
| "≈5 Tausend", |
| "≈5 Tausend", |
| "5 Tausend – 5 Millionen"); |
| |
| assertFormatRange( |
| "Unit collapse, long-form compact notation", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.UNIT) |
| .numberFormatterBoth(NumberFormatter.with().notation(Notation.compactLong())), |
| new ULocale("de-CH"), |
| "1–5", |
| "≈5", |
| "≈5", |
| "0–3", |
| "≈0", |
| "3–3 Tausend", |
| "3 Tausend – 5 Tausend", |
| "≈5 Tausend", |
| "≈5 Tausend", |
| "5 Tausend – 5 Millionen"); |
| |
| assertFormatRange( |
| "Default collapse on measurement unit with compact-short notation", |
| NumberRangeFormatter.with() |
| .numberFormatterBoth(NumberFormatter.with().notation(Notation.compactShort()).unit(MeasureUnit.METER)), |
| new ULocale("en-us"), |
| "1–5 m", |
| "~5 m", |
| "~5 m", |
| "0–3 m", |
| "~0 m", |
| "3–3K m", |
| "3K – 5K m", |
| "~5K m", |
| "~5K m", |
| "5K – 5M m"); |
| |
| assertFormatRange( |
| "No collapse on measurement unit with compact-short notation", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.NONE) |
| .numberFormatterBoth(NumberFormatter.with().notation(Notation.compactShort()).unit(MeasureUnit.METER)), |
| new ULocale("en-us"), |
| "1 m – 5 m", |
| "~5 m", |
| "~5 m", |
| "0 m – 3 m", |
| "~0 m", |
| "3 m – 3K m", |
| "3K m – 5K m", |
| "~5K m", |
| "~5K m", |
| "5K m – 5M m"); |
| |
| assertFormatRange( |
| "Unit collapse on measurement unit with compact-short notation", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.UNIT) |
| .numberFormatterBoth(NumberFormatter.with().notation(Notation.compactShort()).unit(MeasureUnit.METER)), |
| new ULocale("en-us"), |
| "1–5 m", |
| "~5 m", |
| "~5 m", |
| "0–3 m", |
| "~0 m", |
| "3–3K m", |
| "3K – 5K m", |
| "~5K m", |
| "~5K m", |
| "5K – 5M m"); |
| |
| assertFormatRange( |
| "All collapse on measurement unit with compact-short notation", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.ALL) |
| .numberFormatterBoth(NumberFormatter.with().notation(Notation.compactShort()).unit(MeasureUnit.METER)), |
| new ULocale("en-us"), |
| "1–5 m", |
| "~5 m", |
| "~5 m", |
| "0–3 m", |
| "~0 m", |
| "3–3K m", |
| "3–5K m", // this one is the key use case for ALL |
| "~5K m", |
| "~5K m", |
| "5K – 5M m"); |
| |
| assertFormatRange( |
| "No collapse on scientific notation", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.NONE) |
| .numberFormatterBoth(NumberFormatter.with().notation(Notation.scientific())), |
| new ULocale("en-us"), |
| "1E0 – 5E0", |
| "~5E0", |
| "~5E0", |
| "0E0 – 3E0", |
| "~0E0", |
| "3E0 – 3E3", |
| "3E3 – 5E3", |
| "4.999E3 – 5.001E3", |
| "~5E3", |
| "5E3 – 5E6"); |
| |
| assertFormatRange( |
| "All collapse on scientific notation", |
| NumberRangeFormatter.with() |
| .collapse(RangeCollapse.ALL) |
| .numberFormatterBoth(NumberFormatter.with().notation(Notation.scientific())), |
| new ULocale("en-us"), |
| "1–5E0", |
| "~5E0", |
| "~5E0", |
| "0–3E0", |
| "~0E0", |
| "3E0 – 3E3", |
| "3–5E3", |
| "4.999–5.001E3", |
| "~5E3", |
| "5E3 – 5E6"); |
| |
| // TODO: Test compact currency? |
| // The code is not smart enough to differentiate the notation from the unit. |
| } |
| |
| @Test |
| public void testIdentity() { |
| assertFormatRange( |
| "Identity fallback Range", |
| NumberRangeFormatter.with().identityFallback(RangeIdentityFallback.RANGE), |
| new ULocale("en-us"), |
| "1–5", |
| "5–5", |
| "5–5", |
| "0–3", |
| "0–0", |
| "3–3,000", |
| "3,000–5,000", |
| "4,999–5,001", |
| "5,000–5,000", |
| "5,000–5,000,000"); |
| |
| assertFormatRange( |
| "Identity fallback Approximately or Single Value", |
| NumberRangeFormatter.with().identityFallback(RangeIdentityFallback.APPROXIMATELY_OR_SINGLE_VALUE), |
| new ULocale("en-us"), |
| "1–5", |
| "~5", |
| "5", |
| "0–3", |
| "0", |
| "3–3,000", |
| "3,000–5,000", |
| "4,999–5,001", |
| "5,000", |
| "5,000–5,000,000"); |
| |
| assertFormatRange( |
| "Identity fallback Single Value", |
| NumberRangeFormatter.with().identityFallback(RangeIdentityFallback.SINGLE_VALUE), |
| new ULocale("en-us"), |
| "1–5", |
| "5", |
| "5", |
| "0–3", |
| "0", |
| "3–3,000", |
| "3,000–5,000", |
| "4,999–5,001", |
| "5,000", |
| "5,000–5,000,000"); |
| |
| assertFormatRange( |
| "Identity fallback Approximately or Single Value with compact notation", |
| NumberRangeFormatter.with() |
| .identityFallback(RangeIdentityFallback.APPROXIMATELY_OR_SINGLE_VALUE) |
| .numberFormatterBoth(NumberFormatter.with().notation(Notation.compactShort())), |
| new ULocale("en-us"), |
| "1–5", |
| "~5", |
| "5", |
| "0–3", |
| "0", |
| "3–3K", |
| "3K – 5K", |
| "~5K", |
| "5K", |
| "5K – 5M"); |
| |
| assertFormatRange( |
| "Approximately in middle of unit string", |
| NumberRangeFormatter.with().numberFormatterBoth( |
| NumberFormatter.with().unit(MeasureUnit.FAHRENHEIT).unitWidth(UnitWidth.FULL_NAME)), |
| new ULocale("zh-Hant"), |
| "華氏 1-5 度", |
| "華氏 ~5 度", |
| "華氏 ~5 度", |
| "華氏 0-3 度", |
| "華氏 ~0 度", |
| "華氏 3-3,000 度", |
| "華氏 3,000-5,000 度", |
| "華氏 4,999-5,001 度", |
| "華氏 ~5,000 度", |
| "華氏 5,000-5,000,000 度"); |
| } |
| |
| @Test |
| public void testDifferentFormatters() { |
| assertFormatRange( |
| "Different rounding rules", |
| NumberRangeFormatter.with() |
| .numberFormatterFirst(NumberFormatter.with().precision(Precision.integer())) |
| .numberFormatterSecond(NumberFormatter.with().precision(Precision.fixedSignificantDigits(2))), |
| new ULocale("en-us"), |
| "1–5.0", |
| "5–5.0", |
| "5–5.0", |
| "0–3.0", |
| "0–0.0", |
| "3–3,000", |
| "3,000–5,000", |
| "4,999–5,000", |
| "5,000–5,000", // TODO: Should this one be ~5,000? |
| "5,000–5,000,000"); |
| } |
| |
| @Test |
| public void testPlurals() { |
| // Locale sl has interesting plural forms: |
| // GBP{ |
| // one{"britanski funt"} |
| // two{"britanska funta"} |
| // few{"britanski funti"} |
| // other{"britanskih funtov"} |
| // } |
| ULocale locale = new ULocale("sl"); |
| |
| UnlocalizedNumberFormatter unf = NumberFormatter.with() |
| .unit(GBP) |
| .unitWidth(UnitWidth.FULL_NAME) |
| .precision(Precision.integer()); |
| LocalizedNumberFormatter lnf = unf.locale(locale); |
| |
| // For comparison, run the non-range version of the formatter |
| assertEquals(Integer.toString(1), "1 britanski funt", lnf.format(1).toString()); |
| assertEquals(Integer.toString(2), "2 britanska funta", lnf.format(2).toString()); |
| assertEquals(Integer.toString(3), "3 britanski funti", lnf.format(3).toString()); |
| assertEquals(Integer.toString(5), "5 britanskih funtov", lnf.format(5).toString()); |
| |
| LocalizedNumberRangeFormatter lnrf = NumberRangeFormatter.with() |
| .numberFormatterBoth(unf) |
| .identityFallback(RangeIdentityFallback.RANGE) |
| .locale(locale); |
| |
| Object[][] cases = new Object[][] { |
| {1, 1, "1–1 britanski funti"}, // one + one -> few |
| {1, 2, "1–2 britanska funta"}, // one + two -> two |
| {1, 3, "1–3 britanski funti"}, // one + few -> few |
| {1, 5, "1–5 britanskih funtov"}, // one + other -> other |
| {2, 1, "2–1 britanski funti"}, // two + one -> few |
| {2, 2, "2–2 britanska funta"}, // two + two -> two |
| {2, 3, "2–3 britanski funti"}, // two + few -> few |
| {2, 5, "2–5 britanskih funtov"}, // two + other -> other |
| {3, 1, "3–1 britanski funti"}, // few + one -> few |
| {3, 2, "3–2 britanska funta"}, // few + two -> two |
| {3, 3, "3–3 britanski funti"}, // few + few -> few |
| {3, 5, "3–5 britanskih funtov"}, // few + other -> other |
| {5, 1, "5–1 britanski funti"}, // other + one -> few |
| {5, 2, "5–2 britanska funta"}, // other + two -> two |
| {5, 3, "5–3 britanski funti"}, // other + few -> few |
| {5, 5, "5–5 britanskih funtov"}, // other + other -> other |
| }; |
| for (Object[] cas : cases) { |
| int first = (Integer) cas[0]; |
| int second = (Integer) cas[1]; |
| String expected = (String) cas[2]; |
| String message = Integer.toString(first) + " " + Integer.toString(second); |
| String actual = lnrf.formatRange(first, second).toString(); |
| assertEquals(message, expected, actual); |
| } |
| } |
| |
| @Test |
| public void testFieldPositions() { |
| { |
| String message = "Field position test 1"; |
| String expectedString = "3K – 5K m"; |
| FormattedNumberRange fmtd = assertFormattedRangeEquals( |
| message, |
| NumberRangeFormatter.with() |
| .numberFormatterBoth(NumberFormatter.with() |
| .unit(MeasureUnit.METER) |
| .notation(Notation.compactShort())) |
| .locale(ULocale.US), |
| 3000, |
| 5000, |
| expectedString); |
| Object[][] expectedFieldPositions = new Object[][]{ |
| {NumberFormat.Field.INTEGER, 0, 1}, |
| {NumberFormat.Field.COMPACT, 1, 2}, |
| {NumberFormat.Field.INTEGER, 5, 6}, |
| {NumberFormat.Field.COMPACT, 6, 7}, |
| {NumberFormat.Field.MEASURE_UNIT, 8, 9}}; |
| FormattedValueTest.checkFormattedValue(message, fmtd, expectedString, expectedFieldPositions); |
| } |
| |
| { |
| String message = "Field position test 2"; |
| String expectedString = "87,654,321–98,765,432"; |
| FormattedNumberRange fmtd = assertFormattedRangeEquals( |
| message, |
| NumberRangeFormatter.withLocale(ULocale.US), |
| 87654321, |
| 98765432, |
| expectedString); |
| Object[][] expectedFieldPositions = new Object[][]{ |
| {NumberFormat.Field.GROUPING_SEPARATOR, 2, 3}, |
| {NumberFormat.Field.GROUPING_SEPARATOR, 6, 7}, |
| {NumberFormat.Field.INTEGER, 0, 10}, |
| {NumberFormat.Field.GROUPING_SEPARATOR, 13, 14}, |
| {NumberFormat.Field.GROUPING_SEPARATOR, 17, 18}, |
| {NumberFormat.Field.INTEGER, 11, 21}}; |
| FormattedValueTest.checkFormattedValue(message, fmtd, expectedString, expectedFieldPositions); |
| } |
| } |
| |
| static final String[] allNSNames = NumberingSystem.getAvailableNames(); |
| |
| private class RangePatternSink extends UResource.Sink { |
| Map<String,String> rangePatterns = new HashMap<>(); |
| Map<String,String> approxPatterns = new HashMap<>(); |
| |
| // NumberElements{ latn{ miscPatterns{ range{"{0}-{1}"} } } } |
| @Override |
| public void put(UResource.Key key, UResource.Value value, boolean noFallback) { |
| UResource.Table numberElementsTable = value.getTable(); |
| for (int i = 0; numberElementsTable.getKeyAndValue(i, key, value); ++i) { |
| String nsName = key.toString(); |
| if (Arrays.binarySearch(allNSNames, nsName) < 0) { |
| continue; |
| } |
| UResource.Table nsTable = value.getTable(); |
| for (int j = 0; nsTable.getKeyAndValue(j, key, value); ++j) { |
| if (!key.contentEquals("miscPatterns")) { |
| continue; |
| } |
| UResource.Table miscTable = value.getTable(); |
| for (int k = 0; miscTable.getKeyAndValue(k, key, value); ++k) { |
| if (key.contentEquals("range") && !rangePatterns.containsKey(nsName)) { |
| rangePatterns.put(nsName, value.getString()); |
| } |
| if (key.contentEquals("approximately") && !approxPatterns.containsKey(nsName)) { |
| approxPatterns.put(nsName, value.getString()); |
| } |
| } |
| } |
| } |
| } |
| |
| public void checkAndReset(ULocale locale) { |
| // NOTE: If this test ever starts failing, there might not need to |
| // be any changes made to NumberRangeFormatter. Please add a new |
| // test demonstrating how different numbering systems in the same |
| // locale produce different results in NumberRangeFormatter, and |
| // then you can disable or delete this test. |
| // Additional context: ICU-20144 |
| |
| Set<String> allRangePatterns = new HashSet<>(); |
| allRangePatterns.addAll(rangePatterns.values()); |
| assertEquals("Should have only one unique range pattern: " + locale + ": " + rangePatterns, |
| 1, allRangePatterns.size()); |
| |
| Set<String> allApproxPatterns = new HashSet<>(); |
| allApproxPatterns.addAll(approxPatterns.values()); |
| assertEquals("Should have only one unique approximately pattern: " + locale + ": " + approxPatterns, |
| 1, allApproxPatterns.size()); |
| |
| rangePatterns.clear(); |
| approxPatterns.clear(); |
| } |
| } |
| |
| @Test |
| public void testNumberingSystemRangeData() { |
| RangePatternSink sink = new RangePatternSink(); |
| for (ULocale locale : ULocale.getAvailableLocales()) { |
| ICUResourceBundle resource = (ICUResourceBundle) |
| UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale); |
| resource.getAllItemsWithFallback("NumberElements", sink); |
| sink.checkAndReset(locale); |
| } |
| } |
| |
| static void assertFormatRange( |
| String message, |
| UnlocalizedNumberRangeFormatter f, |
| ULocale locale, |
| String expected_10_50, |
| String expected_49_51, |
| String expected_50_50, |
| String expected_00_30, |
| String expected_00_00, |
| String expected_30_3K, |
| String expected_30K_50K, |
| String expected_49K_51K, |
| String expected_50K_50K, |
| String expected_50K_50M) { |
| LocalizedNumberRangeFormatter l = f.locale(locale); |
| assertFormattedRangeEquals(message, l, 1, 5, expected_10_50); |
| assertFormattedRangeEquals(message, l, 4.9999999, 5.0000001, expected_49_51); |
| assertFormattedRangeEquals(message, l, 5, 5, expected_50_50); |
| assertFormattedRangeEquals(message, l, 0, 3, expected_00_30); |
| assertFormattedRangeEquals(message, l, 0, 0, expected_00_00); |
| assertFormattedRangeEquals(message, l, 3, 3000, expected_30_3K); |
| assertFormattedRangeEquals(message, l, 3000, 5000, expected_30K_50K); |
| assertFormattedRangeEquals(message, l, 4999, 5001, expected_49K_51K); |
| assertFormattedRangeEquals(message, l, 5000, 5000, expected_50K_50K); |
| assertFormattedRangeEquals(message, l, 5e3, 5e6, expected_50K_50M); |
| } |
| |
| private static FormattedNumberRange assertFormattedRangeEquals(String message, LocalizedNumberRangeFormatter l, Number first, |
| Number second, String expected) { |
| FormattedNumberRange fnr = l.formatRange(first, second); |
| String actual = fnr.toString(); |
| assertEquals(message + ": " + first + ", " + second, expected, actual); |
| return fnr; |
| } |
| |
| } |