From 47e1aaaf143f487480d7059c682c82ac13d00ecb Mon Sep 17 00:00:00 2001 From: Peter Edberg Date: Sat, 21 Jan 2017 01:03:32 +0000 Subject: [PATCH] ICU-12763 Add uplrules_selectWithFormat and PluralRules::select that take & use [U]NumberFormat X-SVN-Rev: 39591 --- icu4c/source/i18n/plurrule.cpp | 21 +++++- icu4c/source/i18n/unicode/plurrule.h | 16 +++++ icu4c/source/i18n/unicode/upluralrules.h | 30 +++++++++ icu4c/source/i18n/upluralrules.cpp | 22 +++++++ icu4c/source/test/cintltst/cpluralrulestest.c | 66 +++++++++++++------ 5 files changed, 135 insertions(+), 20 deletions(-) diff --git a/icu4c/source/i18n/plurrule.cpp b/icu4c/source/i18n/plurrule.cpp index 1362eb5d078..0ce378cba9d 100644 --- a/icu4c/source/i18n/plurrule.cpp +++ b/icu4c/source/i18n/plurrule.cpp @@ -17,6 +17,8 @@ #include "unicode/plurrule.h" #include "unicode/upluralrules.h" #include "unicode/ures.h" +#include "unicode/numfmt.h" +#include "unicode/decimfmt.h" #include "charstr.h" #include "cmemory.h" #include "cstring.h" @@ -36,7 +38,6 @@ #include "digitinterval.h" #include "visibledigits.h" - #if !UCONFIG_NO_FORMATTING U_NAMESPACE_BEGIN @@ -246,6 +247,24 @@ PluralRules::select(double number) const { return select(FixedDecimal(number)); } +UnicodeString +PluralRules::select(const Formattable& obj, const NumberFormat& fmt) const { + UErrorCode status = U_ZERO_ERROR; + const DecimalFormat *decFmt = dynamic_cast(&fmt); + if (decFmt != NULL) { + VisibleDigitsWithExponent digits; + decFmt->initVisibleDigitsWithExponent(obj, digits, status); + if (U_SUCCESS(status)) { + return select(digits); + } + } + double number = obj.getDouble(status); + if (U_SUCCESS(status)) { + return select(number); + } + return getKeywordOther(); +} + UnicodeString PluralRules::select(const FixedDecimal &number) const { if (mRules == NULL) { diff --git a/icu4c/source/i18n/unicode/plurrule.h b/icu4c/source/i18n/unicode/plurrule.h index d56b23ab4ba..a46abee85e6 100644 --- a/icu4c/source/i18n/unicode/plurrule.h +++ b/icu4c/source/i18n/unicode/plurrule.h @@ -29,6 +29,9 @@ #include "unicode/format.h" #include "unicode/upluralrules.h" +#ifndef U_HIDE_INTERNAL_API +#include "unicode/numfmt.h" +#endif /* U_HIDE_INTERNAL_API */ /** * Value returned by PluralRules::getUniqueKeywordValue() when there is no @@ -345,6 +348,19 @@ public: UnicodeString select(double number) const; #ifndef U_HIDE_INTERNAL_API + /** + * Given a number and a format, returns the keyword of the first applicable + * rule for this PluralRules object. + * Note: This internal preview interface may be removed in the future if + * an architecturally cleaner solution reaches stable status. + * @param obj The numeric object for which the rule should be determined. + * @param fmt The NumberFormat specifying how the number will be formatted + * (this can affect the plural form, e.g. "1 dollar" vs "1.0 dollars"). + * @return The keyword of the selected rule. + * @internal ICU 59 technology preview, may be removed in the future + */ + UnicodeString select(const Formattable& obj, const NumberFormat& fmt) const; + /** * @internal */ diff --git a/icu4c/source/i18n/unicode/upluralrules.h b/icu4c/source/i18n/unicode/upluralrules.h index f9b2668d198..7f2d000c95a 100644 --- a/icu4c/source/i18n/unicode/upluralrules.h +++ b/icu4c/source/i18n/unicode/upluralrules.h @@ -15,6 +15,9 @@ #if !UCONFIG_NO_FORMATTING #include "unicode/localpointer.h" +#ifndef U_HIDE_INTERNAL_API +#include "unicode/unum.h" +#endif /* U_HIDE_INTERNAL_API */ /** * \file @@ -144,6 +147,33 @@ uplrules_select(const UPluralRules *uplrules, 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 + * number, according to the UPluralRules object and given the number format + * specified by the UNumberFormat object. + * Note: This internal preview interface may be removed in the future if + * an architecturally cleaner solution reaches stable status. + * @param uplrules The UPluralRules object specifying the rules. + * @param number The number for which the rule has to be determined. + * @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 capacity The capacity of the keyword buffer. + * @param status A pointer to a UErrorCode to receive any errors. + * @return The length of keyword. + * @internal ICU 59 technology preview, may be removed in the future + */ +U_INTERNAL int32_t U_EXPORT2 +uplrules_selectWithFormat(const UPluralRules *uplrules, + double number, + const UNumberFormat *fmt, + UChar *keyword, int32_t capacity, + UErrorCode *status); + +#endif /* U_HIDE_INTERNAL_API */ + #endif /* #if !UCONFIG_NO_FORMATTING */ #endif diff --git a/icu4c/source/i18n/upluralrules.cpp b/icu4c/source/i18n/upluralrules.cpp index 7647637d7c1..384a87c6b05 100644 --- a/icu4c/source/i18n/upluralrules.cpp +++ b/icu4c/source/i18n/upluralrules.cpp @@ -15,6 +15,8 @@ #include "unicode/plurrule.h" #include "unicode/locid.h" #include "unicode/unistr.h" +#include "unicode/unum.h" +#include "unicode/numfmt.h" U_NAMESPACE_USE @@ -54,5 +56,25 @@ uplrules_select(const UPluralRules *uplrules, return result.extract(keyword, capacity, *status); } +U_CAPI int32_t U_EXPORT2 +uplrules_selectWithFormat(const UPluralRules *uplrules, + double number, + const UNumberFormat *fmt, + UChar *keyword, int32_t capacity, + UErrorCode *status) +{ + if (U_FAILURE(*status)) { + return 0; + } + const PluralRules* plrules = reinterpret_cast(uplrules); + const NumberFormat* nf = reinterpret_cast(fmt); + if (plrules == NULL || nf == NULL || ((keyword == NULL)? capacity != 0 : capacity < 0)) { + *status = U_ILLEGAL_ARGUMENT_ERROR; + return 0; + } + Formattable obj(number); + UnicodeString result = plrules->select(obj, *nf); + return result.extract(keyword, capacity, *status); +} #endif /* #if !UCONFIG_NO_FORMATTING */ diff --git a/icu4c/source/test/cintltst/cpluralrulestest.c b/icu4c/source/test/cintltst/cpluralrulestest.c index 5582ff2ecac..1a784a9f5a8 100644 --- a/icu4c/source/test/cintltst/cpluralrulestest.c +++ b/icu4c/source/test/cintltst/cpluralrulestest.c @@ -32,31 +32,34 @@ typedef struct { const char * locale; double number; const char * keywordExpected; + const char * keywordExpectedForDecimals; } PluralRulesTestItem; /* Just a small set of tests for now, other functionality is tested in the C++ tests */ static const PluralRulesTestItem testItems[] = { - { "en", 0, "other" }, - { "en", 0.5, "other" }, - { "en", 1, "one" }, - { "en", 1.5, "other" }, - { "en", 2, "other" }, - { "fr", 0, "one" }, - { "fr", 0.5, "one" }, - { "fr", 1, "one" }, - { "fr", 1.5, "one" }, - { "fr", 2, "other" }, - { "ru", 0, "many" }, - { "ru", 0.5, "other" }, - { "ru", 1, "one" }, - { "ru", 1.5, "other" }, - { "ru", 2, "few" }, - { "ru", 5, "many" }, - { "ru", 10, "many" }, - { "ru", 11, "many" }, - { NULL, 0, NULL } + { "en", 0, "other", "other" }, + { "en", 0.5, "other", "other" }, + { "en", 1, "one", "other" }, + { "en", 1.5, "other", "other" }, + { "en", 2, "other", "other" }, + { "fr", 0, "one", "one" }, + { "fr", 0.5, "one", "one" }, + { "fr", 1, "one", "one" }, + { "fr", 1.5, "one", "one" }, + { "fr", 2, "other", "other" }, + { "ru", 0, "many", "other" }, + { "ru", 0.5, "other", "other" }, + { "ru", 1, "one", "other" }, + { "ru", 1.5, "other", "other" }, + { "ru", 2, "few", "other" }, + { "ru", 5, "many", "other" }, + { "ru", 10, "many", "other" }, + { "ru", 11, "many", "other" }, + { NULL, 0, NULL, NULL } }; +static const UChar twoDecimalPat[] = { 0x23,0x30,0x2E,0x30,0x30,0 }; /* "#0.00" */ + enum { kKeywordBufLen = 32 }; @@ -69,6 +72,7 @@ static void TestPluralRules() UErrorCode status = U_ZERO_ERROR; UPluralRules* uplrules = uplrules_open(testItemPtr->locale, &status); if ( U_SUCCESS(status) ) { + UNumberFormat* unumfmt; UChar keyword[kKeywordBufLen]; UChar keywordExpected[kKeywordBufLen]; int32_t keywdLen = uplrules_select(uplrules, testItemPtr->number, keyword, kKeywordBufLen, &status); @@ -86,6 +90,30 @@ static void TestPluralRules() log_err("FAIL: uplrules_select for locale %s, number %.1f: %s\n", testItemPtr->locale, testItemPtr->number, myErrorName(status) ); } + + status = U_ZERO_ERROR; + unumfmt = unum_open(UNUM_PATTERN_DECIMAL, twoDecimalPat, -1, testItemPtr->locale, NULL, &status); + if ( U_SUCCESS(status) ) { + keywdLen = uplrules_selectWithFormat(uplrules, testItemPtr->number, unumfmt, keyword, kKeywordBufLen, &status); + if (keywdLen >= kKeywordBufLen) { + keyword[kKeywordBufLen-1] = 0; + } + if ( U_SUCCESS(status) ) { + u_unescape(testItemPtr->keywordExpectedForDecimals, keywordExpected, kKeywordBufLen); + if ( u_strcmp(keyword, keywordExpected) != 0 ) { + char bcharBuf[kKeywordBufLen]; + log_data_err("ERROR: uplrules_selectWithFormat for locale %s, number %.1f: expect %s, get %s\n", + testItemPtr->locale, testItemPtr->number, testItemPtr->keywordExpectedForDecimals, u_austrcpy(bcharBuf,keyword) ); + } + } else { + log_err("FAIL: uplrules_selectWithFormat for locale %s, number %.1f: %s\n", + testItemPtr->locale, testItemPtr->number, myErrorName(status) ); + } + unum_close(unumfmt); + } else { + log_err("FAIL: unum_open for locale %s: %s\n", testItemPtr->locale, myErrorName(status) ); + } + uplrules_close(uplrules); } else { log_err("FAIL: uplrules_open for locale %s: %s\n", testItemPtr->locale, myErrorName(status) );