- Makes new dependency class for PluralRules+FormattedNumber.
sharedbreakiterator.o scientificnumberformatter.o dayperiodrules.o nounit.o \
number_affixutils.o number_compact.o number_decimalquantity.o \
number_decimfmtprops.o number_fluent.o number_formatimpl.o number_grouping.o \
-number_integerwidth.o number_longnames.o number_modifiers.o number_notation.o \
+number_integerwidth.o number_longnames.o number_modifiers.o number_notation.o number_output.o \
number_padding.o number_patternmodifier.o number_patternstring.o \
number_rounding.o number_scientific.o number_stringbuilder.o number_utils.o number_asformat.o \
number_mapper.o number_multiplier.o number_currencysymbols.o number_skeletons.o number_capi.o \
<ClCompile Include="number_longnames.cpp" />
<ClCompile Include="number_modifiers.cpp" />
<ClCompile Include="number_notation.cpp" />
+ <ClCompile Include="number_output.cpp" />
<ClCompile Include="number_padding.cpp" />
<ClCompile Include="number_patternmodifier.cpp" />
<ClCompile Include="number_patternstring.cpp" />
<ClCompile Include="number_notation.cpp">
<Filter>formatting</Filter>
</ClCompile>
+ <ClCompile Include="number_output.cpp">
+ <Filter>formatting</Filter>
+ </ClCompile>
<ClCompile Include="number_padding.cpp">
<Filter>formatting</Filter>
</ClCompile>
<ClCompile Include="number_longnames.cpp" />
<ClCompile Include="number_modifiers.cpp" />
<ClCompile Include="number_notation.cpp" />
+ <ClCompile Include="number_output.cpp" />
<ClCompile Include="number_padding.cpp" />
<ClCompile Include="number_patternmodifier.cpp" />
<ClCompile Include="number_patternstring.cpp" />
U_NAMESPACE_END
+const DecimalQuantity* icu::number::impl::validateUFormattedNumberToDecimalQuantity(
+ const UFormattedNumber* uresult, UErrorCode& status) {
+ auto* result = UFormattedNumberApiHelper::validate(uresult, status);
+ if (U_FAILURE(status)) {
+ return nullptr;
+ }
+ return &result->fData.quantity;
+}
+
+
U_CAPI UNumberFormatter* U_EXPORT2
unumf_openForSkeletonAndLocale(const UChar* skeleton, int32_t skeletonLen, const char* locale,
}
-FormattedNumber::FormattedNumber(FormattedNumber&& src) U_NOEXCEPT
- : fResults(src.fResults), fErrorCode(src.fErrorCode) {
- // Disown src.fResults to prevent double-deletion
- src.fResults = nullptr;
- src.fErrorCode = U_INVALID_STATE_ERROR;
-}
-
-FormattedNumber& FormattedNumber::operator=(FormattedNumber&& src) U_NOEXCEPT {
- delete fResults;
- fResults = src.fResults;
- fErrorCode = src.fErrorCode;
- // Disown src.fResults to prevent double-deletion
- src.fResults = nullptr;
- src.fErrorCode = U_INVALID_STATE_ERROR;
- return *this;
-}
-
-UnicodeString FormattedNumber::toString(UErrorCode& status) const {
- if (U_FAILURE(status)) {
- return ICU_Utility::makeBogusString();
- }
- if (fResults == nullptr) {
- status = fErrorCode;
- return ICU_Utility::makeBogusString();
- }
- return fResults->string.toUnicodeString();
-}
-
-UnicodeString FormattedNumber::toTempString(UErrorCode& status) const {
- if (U_FAILURE(status)) {
- return ICU_Utility::makeBogusString();
- }
- if (fResults == nullptr) {
- status = fErrorCode;
- return ICU_Utility::makeBogusString();
- }
- return fResults->string.toTempUnicodeString();
-}
-
-Appendable& FormattedNumber::appendTo(Appendable& appendable, UErrorCode& status) const {
- if (U_FAILURE(status)) {
- return appendable;
- }
- if (fResults == nullptr) {
- status = fErrorCode;
- return appendable;
- }
- appendable.appendString(fResults->string.chars(), fResults->string.length());
- return appendable;
-}
-
-UBool FormattedNumber::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const {
- if (U_FAILURE(status)) {
- return FALSE;
- }
- if (fResults == nullptr) {
- status = fErrorCode;
- return FALSE;
- }
- // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
- return fResults->string.nextPosition(cfpos, status) ? TRUE : FALSE;
-}
-
-UBool FormattedNumber::nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const {
- if (U_FAILURE(status)) {
- return FALSE;
- }
- if (fResults == nullptr) {
- status = fErrorCode;
- return FALSE;
- }
- // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
- return fResults->string.nextFieldPosition(fieldPosition, status) ? TRUE : FALSE;
-}
-
-void FormattedNumber::getAllFieldPositions(FieldPositionIterator& iterator, UErrorCode& status) const {
- FieldPositionIteratorHandler fpih(&iterator, status);
- getAllFieldPositionsImpl(fpih, status);
-}
-
-void FormattedNumber::getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih,
- UErrorCode& status) const {
- if (U_FAILURE(status)) {
- return;
- }
- if (fResults == nullptr) {
- status = fErrorCode;
- return;
- }
- fResults->string.getAllFieldPositions(fpih, status);
-}
-
-void FormattedNumber::getDecimalQuantity(DecimalQuantity& output, UErrorCode& status) const {
- if (U_FAILURE(status)) {
- return;
- }
- if (fResults == nullptr) {
- status = fErrorCode;
- return;
- }
- output = fResults->quantity;
-}
-
-FormattedNumber::~FormattedNumber() {
- delete fResults;
-}
-
#endif /* #if !UCONFIG_NO_FORMATTING */
--- /dev/null
+// © 2019 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 "unicode/numberformatter.h"
+#include "number_utypes.h"
+#include "util.h"
+#include "number_decimalquantity.h"
+
+U_NAMESPACE_BEGIN
+namespace number {
+
+
+FormattedNumber::FormattedNumber(FormattedNumber&& src) U_NOEXCEPT
+ : fResults(src.fResults), fErrorCode(src.fErrorCode) {
+ // Disown src.fResults to prevent double-deletion
+ src.fResults = nullptr;
+ src.fErrorCode = U_INVALID_STATE_ERROR;
+}
+
+FormattedNumber& FormattedNumber::operator=(FormattedNumber&& src) U_NOEXCEPT {
+ delete fResults;
+ fResults = src.fResults;
+ fErrorCode = src.fErrorCode;
+ // Disown src.fResults to prevent double-deletion
+ src.fResults = nullptr;
+ src.fErrorCode = U_INVALID_STATE_ERROR;
+ return *this;
+}
+
+UnicodeString FormattedNumber::toString(UErrorCode& status) const {
+ if (U_FAILURE(status)) {
+ return ICU_Utility::makeBogusString();
+ }
+ if (fResults == nullptr) {
+ status = fErrorCode;
+ return ICU_Utility::makeBogusString();
+ }
+ return fResults->string.toUnicodeString();
+}
+
+UnicodeString FormattedNumber::toTempString(UErrorCode& status) const {
+ if (U_FAILURE(status)) {
+ return ICU_Utility::makeBogusString();
+ }
+ if (fResults == nullptr) {
+ status = fErrorCode;
+ return ICU_Utility::makeBogusString();
+ }
+ return fResults->string.toTempUnicodeString();
+}
+
+Appendable& FormattedNumber::appendTo(Appendable& appendable, UErrorCode& status) const {
+ if (U_FAILURE(status)) {
+ return appendable;
+ }
+ if (fResults == nullptr) {
+ status = fErrorCode;
+ return appendable;
+ }
+ appendable.appendString(fResults->string.chars(), fResults->string.length());
+ return appendable;
+}
+
+UBool FormattedNumber::nextPosition(ConstrainedFieldPosition& cfpos, UErrorCode& status) const {
+ if (U_FAILURE(status)) {
+ return FALSE;
+ }
+ if (fResults == nullptr) {
+ status = fErrorCode;
+ return FALSE;
+ }
+ // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
+ return fResults->string.nextPosition(cfpos, status) ? TRUE : FALSE;
+}
+
+UBool FormattedNumber::nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const {
+ if (U_FAILURE(status)) {
+ return FALSE;
+ }
+ if (fResults == nullptr) {
+ status = fErrorCode;
+ return FALSE;
+ }
+ // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
+ return fResults->string.nextFieldPosition(fieldPosition, status) ? TRUE : FALSE;
+}
+
+void FormattedNumber::getAllFieldPositions(FieldPositionIterator& iterator, UErrorCode& status) const {
+ FieldPositionIteratorHandler fpih(&iterator, status);
+ getAllFieldPositionsImpl(fpih, status);
+}
+
+void FormattedNumber::getAllFieldPositionsImpl(FieldPositionIteratorHandler& fpih,
+ UErrorCode& status) const {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ if (fResults == nullptr) {
+ status = fErrorCode;
+ return;
+ }
+ fResults->string.getAllFieldPositions(fpih, status);
+}
+
+void FormattedNumber::getDecimalQuantity(impl::DecimalQuantity& output, UErrorCode& status) const {
+ if (U_FAILURE(status)) {
+ return;
+ }
+ if (fResults == nullptr) {
+ status = fErrorCode;
+ return;
+ }
+ output = fResults->quantity;
+}
+
+FormattedNumber::~FormattedNumber() {
+ delete fResults;
+}
+
+
+} // namespace number
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
};
+/** Helper function used in upluralrules.cpp */
+const DecimalQuantity* validateUFormattedNumberToDecimalQuantity(
+ const UFormattedNumber* uresult, UErrorCode& status);
+
+
/**
* Struct for data used by FormattedNumber.
*
#include "sharedpluralrules.h"
#include "unifiedcache.h"
#include "number_decimalquantity.h"
+#include "util.h"
#if !UCONFIG_NO_FORMATTING
return select(FixedDecimal(number));
}
+UnicodeString
+PluralRules::select(const number::FormattedNumber& number, UErrorCode& status) const {
+ DecimalQuantity dq;
+ number.getDecimalQuantity(dq, status);
+ if (U_FAILURE(status)) {
+ return ICU_Utility::makeBogusString();
+ }
+ return select(dq);
+}
+
UnicodeString
PluralRules::select(const IFixedDecimal &number) const {
if (mRules == nullptr) {
class AndConstraint;
class SharedPluralRules;
+namespace number {
+class FormattedNumber;
+}
+
/**
* Defines rules for mapping non-negative numeric values onto a small set of
* keywords. Rules are constructed from a text description, consisting
#endif /* U_HIDE_INTERNAL_API */
/**
- * Given a number, returns the keyword of the first rule that applies to
- * the number. This function can be used with isKeyword* functions to
- * determine the keyword for default plural rules.
+ * Given an integer, returns the keyword of the first rule
+ * that applies to the number. This function can be used with
+ * isKeyword* functions to determine the keyword for default plural rules.
*
* @param number The number for which the rule has to be determined.
* @return The keyword of the selected rule.
UnicodeString select(int32_t number) const;
/**
- * Given a number, returns the keyword of the first rule that applies to
- * the number. This function can be used with isKeyword* functions to
- * determine the keyword for default plural rules.
+ * Given a floating-point number, returns the keyword of the first rule
+ * that applies to the number. This function can be used with
+ * isKeyword* functions to determine the keyword for default plural rules.
*
* @param number The number for which the rule has to be determined.
* @return The keyword of the selected rule.
*/
UnicodeString select(double number) const;
+ /**
+ * Given a formatted number, returns the keyword of the first rule
+ * that applies to the number. This function can be used with
+ * isKeyword* functions to determine the keyword for default plural rules.
+ *
+ * A FormattedNumber allows you to specify an exponent or trailing zeros,
+ * which can affect the plural category. To get a FormattedNumber, see
+ * NumberFormatter.
+ *
+ * @param number The number for which the rule has to be determined.
+ * @param status Set if an error occurs while selecting plural keyword.
+ * This could happen if the FormattedNumber is invalid.
+ * @return The keyword of the selected rule.
+ * @draft ICU 64
+ */
+ UnicodeString select(const number::FormattedNumber& number, UErrorCode& status) const;
+
#ifndef U_HIDE_INTERNAL_API
/**
* @internal
#include "unicode/unum.h"
#endif /* U_HIDE_INTERNAL_API */
+// Forward-declaration
+struct UFormattedNumber;
+
/**
* \file
* \brief C API: Plural rules, select plural keywords for numeric values.
/**
- * Given a number, returns the keyword of the first rule that
+ * Given a floating-point number, returns the keyword of the first rule that
* applies to the number, according to the supplied UPluralRules object.
* @param uplrules The UPluralRules object specifying the rules.
* @param number The number for which the rule has to be determined.
- * @param keyword The keyword of the rule that applies to number.
- * @param capacity The capacity of keyword.
+ * @param keyword An output buffer to write the keyword of the rule that
+ * applies to number.
+ * @param capacity The capacity of the keyword buffer.
* @param status A pointer to a UErrorCode to receive any errors.
- * @return The length of keyword.
+ * @return The length of the keyword.
* @stable ICU 4.8
*/
U_CAPI int32_t U_EXPORT2
UChar *keyword, int32_t capacity,
UErrorCode *status);
+/**
+ * Given a formatted number, returns the keyword of the first rule
+ * that applies to the number, according to the supplied UPluralRules object.
+ *
+ * A UFormattedNumber allows you to specify an exponent or trailing zeros,
+ * which can affect the plural category. To get a UFormattedNumber, see
+ * {@link UNumberFormatter}.
+ *
+ * @param uplrules The UPluralRules object specifying the rules.
+ * @param number The formatted number for which the rule has to be determined.
+ * @param keyword The destination buffer for the keyword of the rule that
+ * applies to number.
+ * @param capacity The capacity of the keyword buffer.
+ * @param status A pointer to a UErrorCode to receive any errors.
+ * @return The length of the keyword.
+ * @draft ICU 64
+ */
+U_CAPI int32_t U_EXPORT2
+uplrules_selectFormatted(const UPluralRules *uplrules,
+ const struct UFormattedNumber* number,
+ UChar *keyword, int32_t capacity,
+ UErrorCode *status);
+
#ifndef U_HIDE_INTERNAL_API
/**
* Given a number, returns the keyword of the first rule that applies to the
* @param fmt The UNumberFormat specifying how the number will be formatted
* (this can affect the plural form, e.g. "1 dollar" vs "1.0 dollars").
* If this is NULL, the function behaves like uplrules_select.
- * @param keyword The keyword of the rule that applies to number.
+ * @param keyword An output buffer to write the keyword of the rule that
+ * applies to number.
* @param capacity The capacity of the keyword buffer.
* @param status A pointer to a UErrorCode to receive any errors.
* @return The length of keyword.
#include "unicode/unistr.h"
#include "unicode/unum.h"
#include "unicode/numfmt.h"
+#include "unicode/unumberformatter.h"
#include "number_decimalquantity.h"
+#include "number_utypes.h"
U_NAMESPACE_USE
return result.extract(keyword, capacity, *status);
}
+U_CAPI int32_t U_EXPORT2
+uplrules_selectFormatted(const UPluralRules *uplrules,
+ const UFormattedNumber* number,
+ UChar *keyword, int32_t capacity,
+ UErrorCode *status)
+{
+ if (U_FAILURE(*status)) {
+ return 0;
+ }
+ if (keyword == NULL ? capacity != 0 : capacity < 0) {
+ *status = U_ILLEGAL_ARGUMENT_ERROR;
+ return 0;
+ }
+ const number::impl::DecimalQuantity* dq =
+ number::impl::validateUFormattedNumberToDecimalQuantity(number, *status);
+ if (U_FAILURE(*status)) {
+ return 0;
+ }
+ UnicodeString result = ((PluralRules*)uplrules)->select(*dq);
+ return result.extract(keyword, capacity, *status);
+}
+
U_CAPI int32_t U_EXPORT2
uplrules_selectWithFormat(const UPluralRules *uplrules,
double number,
#include "unicode/upluralrules.h"
#include "unicode/ustring.h"
#include "unicode/uenum.h"
+#include "unicode/unumberformatter.h"
#include "cintltst.h"
#include "cmemory.h"
#include "cstring.h"
static void TestPluralRules(void);
static void TestOrdinalRules(void);
static void TestGetKeywords(void);
+static void TestFormatted(void);
void addPluralRulesTest(TestNode** root);
TESTCASE(TestPluralRules);
TESTCASE(TestOrdinalRules);
TESTCASE(TestGetKeywords);
+ TESTCASE(TestFormatted);
}
typedef struct {
}
}
+static void TestFormatted() {
+ UErrorCode ec = U_ZERO_ERROR;
+ UNumberFormatter* unumf = NULL;
+ UFormattedNumber* uresult = NULL;
+ UPluralRules* uplrules = NULL;
+
+ uplrules = uplrules_open("hr", &ec);
+ if (!assertSuccess("open plural rules", &ec)) {
+ goto cleanup;
+ }
+
+ unumf = unumf_openForSkeletonAndLocale(u".00", -1, "hr", &ec);
+ if (!assertSuccess("open unumf", &ec)) {
+ goto cleanup;
+ }
+
+ uresult = unumf_openResult(&ec);
+ if (!assertSuccess("open result", &ec)) {
+ goto cleanup;
+ }
+
+ unumf_formatDouble(unumf, 100.2, uresult, &ec);
+ if (!assertSuccess("format", &ec)) {
+ goto cleanup;
+ }
+
+ UChar buffer[40];
+ uplrules_selectFormatted(uplrules, uresult, buffer, 40, &ec);
+ if (!assertSuccess("select", &ec)) {
+ goto cleanup;
+ }
+
+ assertUEquals("0.20 is plural category 'other' in hr", u"other", buffer);
+
+cleanup:
+ uplrules_close(uplrules);
+ unumf_close(unumf);
+ unumf_closeResult(uresult);
+}
+
#endif /* #if !UCONFIG_NO_FORMATTING */
dayperiodrules
listformatter
formatting formattable_cnv regex regex_cnv translit
- double_conversion number_representation numberformatter numberparser
+ double_conversion number_representation number_output numberformatter numberparser
universal_time_scale
uclean_i18n
ucase uniset_core
formatted_value
+group: number_output
+ # PluralRules and FormattedNumber
+ number_output.o
+ standardplural.o plurrule.o
+ deps
+ # FormattedNumber internals:
+ number_representation format
+ # PluralRules internals:
+ unifiedcache
+
group: numberformatter
# ICU 60+ NumberFormatter API
number_affixutils.o number_asformat.o
number_scientific.o number_skeletons.o
currpinf.o dcfmtsym.o numsys.o
numrange_fluent.o numrange_impl.o
- # pluralrules
- standardplural.o plurrule.o
deps
- decnumber double_conversion formattable format units
- number_representation
+ decnumber double_conversion formattable units
+ number_representation number_output
uclean_i18n common
group: numberparser
* others. All Rights Reserved.
********************************************************************************
-* File PLURRULTS.cpp
+* File PLURULTS.cpp
*
********************************************************************************
*/
#include "unicode/localpointer.h"
#include "unicode/plurrule.h"
#include "unicode/stringpiece.h"
+#include "unicode/numberformatter.h"
#include "cmemory.h"
#include "plurrule_impl.h"
TESTCASE_AUTO(testAvailbleLocales);
TESTCASE_AUTO(testParseErrors);
TESTCASE_AUTO(testFixedDecimal);
+ TESTCASE_AUTO(testSelectTrailingZeros);
TESTCASE_AUTO_END;
}
}
+void PluralRulesTest::testSelectTrailingZeros() {
+ IcuTestErrorCode status(*this, "testSelectTrailingZeros");
+ number::UnlocalizedNumberFormatter unf = number::NumberFormatter::with()
+ .precision(number::Precision::fixedFraction(2));
+ struct TestCase {
+ const char* localeName;
+ const char16_t* expectedDoubleKeyword;
+ const char16_t* expectedFormattedKeyword;
+ double number;
+ } cases[] = {
+ {"bs", u"few", u"other", 5.2}, // 5.2 => two, but 5.20 => other
+ {"si", u"one", u"one", 0.0},
+ {"si", u"one", u"one", 1.0},
+ {"si", u"one", u"other", 0.1}, // 0.1 => one, but 0.10 => other
+ {"si", u"one", u"one", 0.01}, // 0.01 => one
+ {"hsb", u"few", u"few", 1.03}, // (f % 100 == 3) => few
+ {"hsb", u"few", u"other", 1.3}, // 1.3 => few, but 1.30 => other
+ };
+ for (const auto& cas : cases) {
+ UnicodeString message(UnicodeString(cas.localeName) + u" " + DoubleToUnicodeString(cas.number));
+ status.setScope(message);
+ Locale locale(cas.localeName);
+ LocalPointer<PluralRules> rules(PluralRules::forLocale(locale, status));
+ assertEquals(message, cas.expectedDoubleKeyword, rules->select(cas.number));
+ number::FormattedNumber fn = unf.locale(locale).formatDouble(cas.number, status);
+ assertEquals(message, cas.expectedFormattedKeyword, rules->select(fn, status));
+ status.errIfFailureAndReset();
+ }
+}
+
+
#endif /* #if !UCONFIG_NO_FORMATTING */
void testAvailbleLocales();
void testParseErrors();
void testFixedDecimal();
+ void testSelectTrailingZeros();
void assertRuleValue(const UnicodeString& rule, double expected);
void assertRuleKeyValue(const UnicodeString& rule, const UnicodeString& key,
import java.util.regex.Pattern;
import com.ibm.icu.impl.PluralRulesLoader;
+import com.ibm.icu.number.FormattedNumber;
+import com.ibm.icu.number.NumberFormatter;
import com.ibm.icu.util.Output;
import com.ibm.icu.util.ULocale;
public int hashCode() {
return rules.hashCode();
}
+
/**
- * Given a number, returns the keyword of the first rule that applies to
- * the number.
+ * Given a floating-point number, returns the keyword of the first rule
+ * that applies to the number.
*
* @param number The number for which the rule has to be determined.
* @return The keyword of the selected rule.
return rules.select(new FixedDecimal(number));
}
+ /**
+ * Given a formatted number, returns the keyword of the first rule that
+ * applies to the number.
+ *
+ * A FormattedNumber allows you to specify an exponent or trailing zeros,
+ * which can affect the plural category. To get a FormattedNumber, see
+ * {@link NumberFormatter}.
+ *
+ * @param number The number for which the rule has to be determined.
+ * @return The keyword of the selected rule.
+ * @draft ICU 64
+ * @provisional This API might change or be removed in a future release.
+ */
+ public String select(FormattedNumber number) {
+ return rules.select(number.getFixedDecimal());
+ }
+
/**
* Given a number, returns the keyword of the first rule that applies to
* the number.
import com.ibm.icu.dev.util.CollectionUtilities;
import com.ibm.icu.impl.Relation;
import com.ibm.icu.impl.Utility;
+import com.ibm.icu.number.FormattedNumber;
+import com.ibm.icu.number.NumberFormatter;
+import com.ibm.icu.number.Precision;
+import com.ibm.icu.number.UnlocalizedNumberFormatter;
import com.ibm.icu.text.NumberFormat;
import com.ibm.icu.text.PluralRules;
import com.ibm.icu.text.PluralRules.FixedDecimal;
public void testUniqueRules() {
main: for (ULocale locale : factory.getAvailableULocales()) {
PluralRules rules = factory.forLocale(locale);
- Map<String, PluralRules> keywordToRule = new HashMap<String, PluralRules>();
- Collection<FixedDecimalSamples> samples = new LinkedHashSet<FixedDecimalSamples>();
+ Map<String, PluralRules> keywordToRule = new HashMap<>();
+ Collection<FixedDecimalSamples> samples = new LinkedHashSet<>();
for (String keyword : rules.getKeywords()) {
for (SampleType sampleType : SampleType.values()) {
@Test
public void testBuiltInRules() {
- // spot check
- PluralRules rules = factory.forLocale(ULocale.US);
- assertEquals("us 0", PluralRules.KEYWORD_OTHER, rules.select(0));
- assertEquals("us 1", PluralRules.KEYWORD_ONE, rules.select(1));
- assertEquals("us 2", PluralRules.KEYWORD_OTHER, rules.select(2));
-
- rules = factory.forLocale(ULocale.JAPAN);
- assertEquals("ja 0", PluralRules.KEYWORD_OTHER, rules.select(0));
- assertEquals("ja 1", PluralRules.KEYWORD_OTHER, rules.select(1));
- assertEquals("ja 2", PluralRules.KEYWORD_OTHER, rules.select(2));
-
- rules = factory.forLocale(ULocale.createCanonical("ru"));
- assertEquals("ru 0", PluralRules.KEYWORD_MANY, rules.select(0));
- assertEquals("ru 1", PluralRules.KEYWORD_ONE, rules.select(1));
- assertEquals("ru 2", PluralRules.KEYWORD_FEW, rules.select(2));
+ Object[][] cases = {
+ {"en-US", PluralRules.KEYWORD_OTHER, 0},
+ {"en-US", PluralRules.KEYWORD_ONE, 1},
+ {"en-US", PluralRules.KEYWORD_OTHER, 2},
+ {"ja-JP", PluralRules.KEYWORD_OTHER, 0},
+ {"ja-JP", PluralRules.KEYWORD_OTHER, 1},
+ {"ja-JP", PluralRules.KEYWORD_OTHER, 2},
+ {"ru", PluralRules.KEYWORD_MANY, 0},
+ {"ru", PluralRules.KEYWORD_ONE, 1},
+ {"ru", PluralRules.KEYWORD_FEW, 2}
+ };
+ for (Object[] cas : cases) {
+ ULocale locale = new ULocale((String) cas[0]);
+ PluralRules rules = factory.forLocale(locale);
+ String expectedKeyword = (String) cas[1];
+ double number = (Integer) cas[2];
+ String message = locale + " " + number;
+ // Check both as double and as FormattedNumber.
+ assertEquals(message, expectedKeyword, rules.select(number));
+ FormattedNumber fn = NumberFormatter.withLocale(locale).format(number);
+ assertEquals(message, expectedKeyword, rules.select(fn));
+ }
+ }
+
+ @Test
+ public void testSelectTrailingZeros() {
+ UnlocalizedNumberFormatter unf = NumberFormatter.with()
+ .precision(Precision.fixedFraction(2));
+ Object[][] cases = {
+ // 1) locale
+ // 2) double expected keyword
+ // 3) formatted number expected keyword (2 fraction digits)
+ // 4) input number
+ {"bs", PluralRules.KEYWORD_FEW, PluralRules.KEYWORD_OTHER, 5.2}, // 5.2 => two, but 5.20 => other
+ {"si", PluralRules.KEYWORD_ONE, PluralRules.KEYWORD_ONE, 0.0},
+ {"si", PluralRules.KEYWORD_ONE, PluralRules.KEYWORD_ONE, 1.0},
+ {"si", PluralRules.KEYWORD_ONE, PluralRules.KEYWORD_OTHER, 0.1}, // 0.1 => one, but 0.10 => other
+ {"si", PluralRules.KEYWORD_ONE, PluralRules.KEYWORD_ONE, 0.01}, // 0.01 => one
+ {"hsb", PluralRules.KEYWORD_FEW, PluralRules.KEYWORD_FEW, 1.03}, // (f % 100 == 3) => few
+ {"hsb", PluralRules.KEYWORD_FEW, PluralRules.KEYWORD_OTHER, 1.3}, // 1.3 => few, but 1.30 => other
+ };
+ for (Object[] cas : cases) {
+ ULocale locale = new ULocale((String) cas[0]);
+ PluralRules rules = factory.forLocale(locale);
+ String expectedDoubleKeyword = (String) cas[1];
+ String expectedFormattedKeyword = (String) cas[2];
+ double number = (Double) cas[3];
+ String message = locale + " " + number;
+ // Check both as double and as FormattedNumber.
+ assertEquals(message, expectedDoubleKeyword, rules.select(number));
+ FormattedNumber fn = unf.locale(locale).format(number);
+ assertEquals(message, expectedFormattedKeyword, rules.select(fn));
+ }
}
@Test
*/
@Test
public void TestGetSamples() {
- Set<ULocale> uniqueRuleSet = new HashSet<ULocale>();
+ Set<ULocale> uniqueRuleSet = new HashSet<>();
for (ULocale locale : factory.getAvailableULocales()) {
uniqueRuleSet.add(PluralRules.getFunctionalEquivalent(locale, null));
}
} else if ("null".equals(valueList)) {
values = null;
} else {
- values = new TreeSet<Double>();
+ values = new TreeSet<>();
for (String value : valueList.split(",")) {
values.add(Double.parseDouble(value));
}
// suppressed in
// INTEGER but not
// DECIMAL
- }, { { "en", new HashSet<Double>(Arrays.asList(1.0d)) }, // check that 1 is suppressed
+ }, { { "en", new HashSet<>(Arrays.asList(1.0d)) }, // check that 1 is suppressed
{ "one", KeywordStatus.SUPPRESSED, null }, { "other", KeywordStatus.UNBOUNDED, null } }, };
- Output<Double> uniqueValue = new Output<Double>();
+ Output<Double> uniqueValue = new Output<>();
for (Object[][] test : tests) {
ULocale locale = new ULocale((String) test[0][0]);
// NumberType numberType = (NumberType) test[1];
};
private void generateLOCALE_SNAPSHOT() {
- Comparator c = new CollectionUtilities.CollectionComparator<Comparable>();
+ Comparator c = new CollectionUtilities.CollectionComparator<>();
Relation<Set<StandardPluralCategories>, PluralRules> setsToRules = Relation.of(
new TreeMap<Set<StandardPluralCategories>, Set<PluralRules>>(c), TreeSet.class, PLURAL_RULE_COMPARATOR);
Relation<PluralRules, ULocale> data = Relation.of(
@Test
public void TestSerialization() {
- Output<Integer> size = new Output<Integer>();
+ Output<Integer> size = new Output<>();
int max = 0;
for (ULocale locale : PluralRules.getAvailableULocales()) {
PluralRules item = PluralRules.forLocale(locale);