ICU-4019 add message format syntax for spelled-out numbers
X-SVN-Rev: 16105
This commit is contained in:
parent
1b339e97db
commit
4255cf1f45
@ -31,6 +31,7 @@
|
||||
#include "unicode/ustring.h"
|
||||
#include "unicode/ucnv_err.h"
|
||||
#include "unicode/uchar.h"
|
||||
#include "unicode/rbnf.h"
|
||||
#include "ustrfmt.h"
|
||||
#include "cmemory.h"
|
||||
#include "uprops.h"
|
||||
@ -48,6 +49,10 @@
|
||||
//---------------------------------------
|
||||
// static data
|
||||
|
||||
static const UChar ID_EMPTY[] = {
|
||||
0 /* empty string, used for default so that null can mark end of list */
|
||||
};
|
||||
|
||||
static const UChar ID_NUMBER[] = {
|
||||
0x6E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0 /* "number" */
|
||||
};
|
||||
@ -60,14 +65,27 @@ static const UChar ID_TIME[] = {
|
||||
static const UChar ID_CHOICE[] = {
|
||||
0x63, 0x68, 0x6F, 0x69, 0x63, 0x65, 0 /* "choice" */
|
||||
};
|
||||
static const UChar ID_SPELLOUT[] = {
|
||||
0x73, 0x70, 0x65, 0x6c, 0x6c, 0x6f, 0x75, 0x74, 0 /* "spellout" */
|
||||
};
|
||||
static const UChar ID_ORDINAL[] = {
|
||||
0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0 /* "ordinal" */
|
||||
};
|
||||
static const UChar ID_DURATION[] = {
|
||||
0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0 /* "duration" */
|
||||
};
|
||||
|
||||
// MessageFormat Type List Number, Date, Time or Choice
|
||||
static const UChar * const TYPE_IDS[] = {
|
||||
NULL,
|
||||
ID_EMPTY,
|
||||
ID_NUMBER,
|
||||
ID_DATE,
|
||||
ID_TIME,
|
||||
ID_CHOICE
|
||||
ID_CHOICE,
|
||||
ID_SPELLOUT,
|
||||
ID_ORDINAL,
|
||||
ID_DURATION,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const UChar ID_CURRENCY[] = {
|
||||
@ -82,13 +100,13 @@ static const UChar ID_INTEGER[] = {
|
||||
|
||||
// NumberFormat modifier list, default, currency, percent or integer
|
||||
static const UChar * const NUMBER_STYLE_IDS[] = {
|
||||
NULL,
|
||||
ID_EMPTY,
|
||||
ID_CURRENCY,
|
||||
ID_PERCENT,
|
||||
ID_INTEGER,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
static const UChar ID_SHORT[] = {
|
||||
0x73, 0x68, 0x6F, 0x72, 0x74, 0 /* "short" */
|
||||
};
|
||||
@ -104,11 +122,12 @@ static const UChar ID_FULL[] = {
|
||||
|
||||
// DateFormat modifier list, default, short, medium, long or full
|
||||
static const UChar * const DATE_STYLE_IDS[] = {
|
||||
NULL,
|
||||
ID_EMPTY,
|
||||
ID_SHORT,
|
||||
ID_MEDIUM,
|
||||
ID_LONG,
|
||||
ID_FULL
|
||||
ID_FULL,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const DateFormat::EStyle DATE_STYLES[] = {
|
||||
@ -119,8 +138,6 @@ static const DateFormat::EStyle DATE_STYLES[] = {
|
||||
DateFormat::kFull,
|
||||
};
|
||||
|
||||
static const int32_t ID_LIST_LENGTH = 5;
|
||||
|
||||
static const int32_t DEFAULT_INITIAL_CAPACITY = 10;
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
@ -1153,6 +1170,17 @@ MessageFormat::parseObject( const UnicodeString& source,
|
||||
}
|
||||
|
||||
// -------------------------------------
|
||||
|
||||
static Format* makeRBNF(URBNFRuleSetTag tag, const Locale& locale, const UnicodeString& defaultRuleSet, UErrorCode& ec) {
|
||||
RuleBasedNumberFormat* fmt = new RuleBasedNumberFormat(tag, locale, ec);
|
||||
if (U_SUCCESS(ec) && defaultRuleSet.length() > 0) {
|
||||
fmt->setDefaultRuleSet(defaultRuleSet, ec);
|
||||
if (U_FAILURE(ec)) { // ignore unrecognized default rule set
|
||||
ec = U_ZERO_ERROR;
|
||||
}
|
||||
}
|
||||
return fmt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the segments[] array (see applyPattern()) and parses the
|
||||
@ -1246,6 +1274,18 @@ MessageFormat::makeFormat(int32_t formatNumber,
|
||||
fmt = new ChoiceFormat(segments[3], parseError, ec);
|
||||
break;
|
||||
|
||||
case 5: // spellout
|
||||
argType = Formattable::kDouble;
|
||||
fmt = makeRBNF(URBNF_SPELLOUT, fLocale, segments[3], ec);
|
||||
break;
|
||||
case 6: // ordinal
|
||||
argType = Formattable::kDouble;
|
||||
fmt = makeRBNF(URBNF_ORDINAL, fLocale, segments[3], ec);
|
||||
break;
|
||||
case 7: // duration
|
||||
argType = Formattable::kDouble;
|
||||
fmt = makeRBNF(URBNF_DURATION, fLocale, segments[3], ec);
|
||||
break;
|
||||
default:
|
||||
argType = Formattable::kString;
|
||||
ec = U_ILLEGAL_ARGUMENT_ERROR;
|
||||
@ -1286,15 +1326,16 @@ int32_t MessageFormat::findKeyword(const UnicodeString& s,
|
||||
const UChar * const *list)
|
||||
{
|
||||
if (s.length() == 0)
|
||||
return 0;
|
||||
return 0; // default
|
||||
|
||||
UnicodeString buffer = s;
|
||||
// Trims the space characters and turns all characters
|
||||
// in s to lower case.
|
||||
buffer.trim().toLower();
|
||||
for (int32_t i = 0; i < ID_LIST_LENGTH; ++i) {
|
||||
if (list[i] && !buffer.compare(list[i], u_strlen(list[i])))
|
||||
for (int32_t i = 0; list[i]; ++i) {
|
||||
if (!buffer.compare(list[i], u_strlen(list[i]))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ TestMessageFormat::runIndexedTest(int32_t index, UBool exec,
|
||||
TESTCASE(15,testAdopt);
|
||||
TESTCASE(16,testCopyConstructor2);
|
||||
TESTCASE(17,TestUnlimitedArgsAndSubformats);
|
||||
TESTCASE(18,TestRBNF);
|
||||
default: name = ""; break;
|
||||
}
|
||||
}
|
||||
@ -1116,4 +1117,59 @@ void TestMessageFormat::TestUnlimitedArgsAndSubformats() {
|
||||
}
|
||||
}
|
||||
|
||||
// test RBNF extensions to message format
|
||||
void TestMessageFormat::TestRBNF(void) {
|
||||
// WARNING: this depends on the RBNF formats for en_US
|
||||
Locale locale("en", "US", "");
|
||||
|
||||
UErrorCode ec = U_ZERO_ERROR;
|
||||
|
||||
UnicodeString values[] = {
|
||||
// decimal values do not format completely for ordinal or duration, and
|
||||
// do not always parse, so do not include them
|
||||
"0", "1", "12", "100", "123", "1001", "123,456", "-17",
|
||||
};
|
||||
int32_t values_count = sizeof(values)/sizeof(values[0]);
|
||||
|
||||
UnicodeString formats[] = {
|
||||
"There are {0,spellout} files to search.",
|
||||
"There are {0,spellout,%simplified} files to search.",
|
||||
"The bogus spellout {0,spellout,%BOGUS} files behaves like the default.",
|
||||
"This is the {0,ordinal} file to search.", // TODO fix bug, ordinal does not parse
|
||||
"Searching this file will take {0,duration} to complete.",
|
||||
"Searching this file will take {0,duration,%with-words} to complete.",
|
||||
};
|
||||
int32_t formats_count = sizeof(formats)/sizeof(formats[0]);
|
||||
|
||||
Formattable args[1];
|
||||
|
||||
NumberFormat* numFmt = NumberFormat::createInstance(locale, ec);
|
||||
for (int i = 0; i < formats_count; ++i) {
|
||||
MessageFormat* fmt = new MessageFormat(formats[i], locale, ec);
|
||||
logln((UnicodeString)"Testing format pattern: '" + formats[i] + "'");
|
||||
for (int j = 0; j < values_count; ++j) {
|
||||
ec = U_ZERO_ERROR;
|
||||
numFmt->parse(values[j], args[0], ec);
|
||||
if (U_FAILURE(ec)) {
|
||||
errln((UnicodeString)"Failed to parse test argument " + values[j]);
|
||||
} else {
|
||||
FieldPosition fp(0);
|
||||
UnicodeString result;
|
||||
fmt->format(args, 1, result, fp, ec);
|
||||
logln((UnicodeString)"value: " + toString(args[0]) + " --> " + result + " ec: " + ec);
|
||||
|
||||
if (i != 3) { // TODO: fix this, for now skip ordinal parsing (format string at index 3)
|
||||
int32_t count = 0;
|
||||
Formattable* parseResult = fmt->parse(result, count, ec);
|
||||
if (count != 1) {
|
||||
errln((UnicodeString)"parse returned " + count + " args");
|
||||
} else if (parseResult[0] != args[0]) {
|
||||
errln((UnicodeString)"parsed argument " + toString(parseResult[0]) + " != " + toString(args[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
@ -63,6 +63,11 @@ public:
|
||||
*/
|
||||
void TestUnlimitedArgsAndSubformats();
|
||||
|
||||
/**
|
||||
* Test RBNF extensions to MessageFormat.
|
||||
*/
|
||||
void TestRBNF();
|
||||
|
||||
//
|
||||
/**
|
||||
* ------------ API tests ----------
|
||||
|
Loading…
Reference in New Issue
Block a user