mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-14 17:24:01 +00:00
ICU-13839 Adding FormattedNumber API to PluralRules.
- Makes new dependency class for PluralRules+FormattedNumber.
This commit is contained in:
parent
dcc1300613
commit
1f85e94068
18 changed files with 421 additions and 149 deletions
|
@ -101,7 +101,7 @@ uregion.o reldatefmt.o quantityformatter.o measunit.o \
|
|||
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 \
|
||||
|
|
|
@ -270,6 +270,7 @@
|
|||
<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" />
|
||||
|
|
|
@ -561,6 +561,9 @@
|
|||
<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>
|
||||
|
|
|
@ -377,6 +377,7 @@
|
|||
<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" />
|
||||
|
|
|
@ -61,6 +61,16 @@ UFormattedNumberImpl::~UFormattedNumberImpl() {
|
|||
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,
|
||||
|
|
|
@ -770,111 +770,4 @@ Format* LocalizedNumberFormatter::toFormat(UErrorCode& status) const {
|
|||
}
|
||||
|
||||
|
||||
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 */
|
||||
|
|
128
icu4c/source/i18n/number_output.cpp
Normal file
128
icu4c/source/i18n/number_output.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
// © 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 */
|
|
@ -27,6 +27,11 @@ struct UFormattedValueImpl : public UMemory, public UFormattedValueApiHelper {
|
|||
};
|
||||
|
||||
|
||||
/** Helper function used in upluralrules.cpp */
|
||||
const DecimalQuantity* validateUFormattedNumberToDecimalQuantity(
|
||||
const UFormattedNumber* uresult, UErrorCode& status);
|
||||
|
||||
|
||||
/**
|
||||
* Struct for data used by FormattedNumber.
|
||||
*
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "sharedpluralrules.h"
|
||||
#include "unifiedcache.h"
|
||||
#include "number_decimalquantity.h"
|
||||
#include "util.h"
|
||||
|
||||
#if !UCONFIG_NO_FORMATTING
|
||||
|
||||
|
@ -264,6 +265,16 @@ PluralRules::select(double number) const {
|
|||
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) {
|
||||
|
|
|
@ -50,6 +50,10 @@ class PluralKeywordEnumeration;
|
|||
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
|
||||
|
@ -323,9 +327,9 @@ public:
|
|||
#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.
|
||||
|
@ -334,9 +338,9 @@ public:
|
|||
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.
|
||||
|
@ -344,6 +348,23 @@ public:
|
|||
*/
|
||||
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
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
#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.
|
||||
|
@ -132,14 +135,15 @@ U_NAMESPACE_END
|
|||
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -148,6 +152,29 @@ uplrules_select(const UPluralRules *uplrules,
|
|||
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
|
||||
|
@ -160,7 +187,8 @@ uplrules_select(const UPluralRules *uplrules,
|
|||
* @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.
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
#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
|
||||
|
||||
|
@ -91,6 +93,28 @@ uplrules_select(const UPluralRules *uplrules,
|
|||
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,
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#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"
|
||||
|
@ -20,6 +21,7 @@
|
|||
static void TestPluralRules(void);
|
||||
static void TestOrdinalRules(void);
|
||||
static void TestGetKeywords(void);
|
||||
static void TestFormatted(void);
|
||||
|
||||
void addPluralRulesTest(TestNode** root);
|
||||
|
||||
|
@ -30,6 +32,7 @@ void addPluralRulesTest(TestNode** root)
|
|||
TESTCASE(TestPluralRules);
|
||||
TESTCASE(TestOrdinalRules);
|
||||
TESTCASE(TestGetKeywords);
|
||||
TESTCASE(TestFormatted);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
|
@ -252,4 +255,44 @@ static void TestGetKeywords() {
|
|||
}
|
||||
}
|
||||
|
||||
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 */
|
||||
|
|
|
@ -840,7 +840,7 @@ library: i18n
|
|||
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
|
||||
|
||||
|
@ -937,6 +937,16 @@ group: number_representation
|
|||
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
|
||||
|
@ -950,11 +960,9 @@ group: numberformatter
|
|||
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
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* others. All Rights Reserved.
|
||||
********************************************************************************
|
||||
|
||||
* File PLURRULTS.cpp
|
||||
* File PLURULTS.cpp
|
||||
*
|
||||
********************************************************************************
|
||||
*/
|
||||
|
@ -22,6 +22,7 @@
|
|||
#include "unicode/localpointer.h"
|
||||
#include "unicode/plurrule.h"
|
||||
#include "unicode/stringpiece.h"
|
||||
#include "unicode/numberformatter.h"
|
||||
|
||||
#include "cmemory.h"
|
||||
#include "plurrule_impl.h"
|
||||
|
@ -53,6 +54,7 @@ void PluralRulesTest::runIndexedTest( int32_t index, UBool exec, const char* &na
|
|||
TESTCASE_AUTO(testAvailbleLocales);
|
||||
TESTCASE_AUTO(testParseErrors);
|
||||
TESTCASE_AUTO(testFixedDecimal);
|
||||
TESTCASE_AUTO(testSelectTrailingZeros);
|
||||
TESTCASE_AUTO_END;
|
||||
}
|
||||
|
||||
|
@ -1004,5 +1006,36 @@ void PluralRulesTest::testFixedDecimal() {
|
|||
}
|
||||
|
||||
|
||||
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 */
|
||||
|
|
|
@ -37,6 +37,7 @@ private:
|
|||
void testAvailbleLocales();
|
||||
void testParseErrors();
|
||||
void testFixedDecimal();
|
||||
void testSelectTrailingZeros();
|
||||
|
||||
void assertRuleValue(const UnicodeString& rule, double expected);
|
||||
void assertRuleKeyValue(const UnicodeString& rule, const UnicodeString& key,
|
||||
|
|
|
@ -29,6 +29,8 @@ import java.util.TreeSet;
|
|||
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;
|
||||
|
||||
|
@ -2026,9 +2028,10 @@ public class PluralRules implements Serializable {
|
|||
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.
|
||||
|
@ -2038,6 +2041,23 @@ public class PluralRules implements Serializable {
|
|||
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.
|
||||
|
|
|
@ -41,6 +41,10 @@ import com.ibm.icu.dev.test.serializable.SerializableTestUtility;
|
|||
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;
|
||||
|
@ -323,8 +327,8 @@ public class PluralRulesTest extends TestFmwk {
|
|||
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()) {
|
||||
|
@ -467,21 +471,59 @@ public class PluralRulesTest extends TestFmwk {
|
|||
|
||||
@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));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
@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
|
||||
|
@ -605,7 +647,7 @@ public class PluralRulesTest extends TestFmwk {
|
|||
*/
|
||||
@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));
|
||||
}
|
||||
|
@ -719,7 +761,7 @@ public class PluralRulesTest extends TestFmwk {
|
|||
} 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));
|
||||
}
|
||||
|
@ -825,9 +867,9 @@ public class PluralRulesTest extends TestFmwk {
|
|||
// 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];
|
||||
|
@ -938,7 +980,7 @@ public class PluralRulesTest extends TestFmwk {
|
|||
};
|
||||
|
||||
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(
|
||||
|
@ -1054,7 +1096,7 @@ public class PluralRulesTest extends TestFmwk {
|
|||
|
||||
@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);
|
||||
|
|
Loading…
Add table
Reference in a new issue