]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/msgfmt.cpp
ICU-66108.tar.gz
[apple/icu.git] / icuSources / i18n / msgfmt.cpp
index 3c2c7c65a1b91213e578b55c9324d2f75284af09..3ca368ef954846521a37d0fb3ff744d48397427e 100644 (file)
@@ -1,6 +1,8 @@
+// © 2016 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
 /********************************************************************
  * COPYRIGHT:
- * Copyright (c) 1997-2012, International Business Machines Corporation and
+ * Copyright (c) 1997-2015, International Business Machines Corporation and
  * others. All Rights Reserved.
  ********************************************************************
  *
@@ -29,6 +31,7 @@
 #include "unicode/decimfmt.h"
 #include "unicode/localpointer.h"
 #include "unicode/msgfmt.h"
+#include "unicode/numberformatter.h"
 #include "unicode/plurfmt.h"
 #include "unicode/rbnf.h"
 #include "unicode/selfmt.h"
 #include "patternprops.h"
 #include "messageimpl.h"
 #include "msgfmt_impl.h"
+#include "plurrule_impl.h"
 #include "uassert.h"
 #include "uelement.h"
 #include "uhash.h"
 #include "ustrfmt.h"
 #include "util.h"
 #include "uvector.h"
+#include "number_decimalquantity.h"
 
 // *****************************************************************************
 // class MessageFormat
@@ -203,6 +208,16 @@ public:
             append(s);
         }
     }
+    void formatAndAppend(const Format* formatter, const Formattable& arg,
+                         const UnicodeString &argString, UErrorCode& ec) {
+        if (!argString.isEmpty()) {
+            if (U_SUCCESS(ec)) {
+                append(argString);
+            }
+        } else {
+            formatAndAppend(formatter, arg, ec);
+        }
+    }
     int32_t length() {
         return len;
     }
@@ -229,8 +244,8 @@ MessageFormat::MessageFormat(const UnicodeString& pattern,
   defaultDateFormat(NULL),
   cachedFormatters(NULL),
   customFormatArgStarts(NULL),
-  pluralProvider(&fLocale, UPLURAL_TYPE_CARDINAL),
-  ordinalProvider(&fLocale, UPLURAL_TYPE_ORDINAL)
+  pluralProvider(*this, UPLURAL_TYPE_CARDINAL),
+  ordinalProvider(*this, UPLURAL_TYPE_ORDINAL)
 {
     setLocaleIDs(fLocale.getName(), fLocale.getName());
     applyPattern(pattern, success);
@@ -251,8 +266,8 @@ MessageFormat::MessageFormat(const UnicodeString& pattern,
   defaultDateFormat(NULL),
   cachedFormatters(NULL),
   customFormatArgStarts(NULL),
-  pluralProvider(&fLocale, UPLURAL_TYPE_CARDINAL),
-  ordinalProvider(&fLocale, UPLURAL_TYPE_ORDINAL)
+  pluralProvider(*this, UPLURAL_TYPE_CARDINAL),
+  ordinalProvider(*this, UPLURAL_TYPE_ORDINAL)
 {
     setLocaleIDs(fLocale.getName(), fLocale.getName());
     applyPattern(pattern, success);
@@ -274,8 +289,8 @@ MessageFormat::MessageFormat(const UnicodeString& pattern,
   defaultDateFormat(NULL),
   cachedFormatters(NULL),
   customFormatArgStarts(NULL),
-  pluralProvider(&fLocale, UPLURAL_TYPE_CARDINAL),
-  ordinalProvider(&fLocale, UPLURAL_TYPE_ORDINAL)
+  pluralProvider(*this, UPLURAL_TYPE_CARDINAL),
+  ordinalProvider(*this, UPLURAL_TYPE_ORDINAL)
 {
     setLocaleIDs(fLocale.getName(), fLocale.getName());
     applyPattern(pattern, parseError, success);
@@ -296,8 +311,8 @@ MessageFormat::MessageFormat(const MessageFormat& that)
   defaultDateFormat(NULL),
   cachedFormatters(NULL),
   customFormatArgStarts(NULL),
-  pluralProvider(&fLocale, UPLURAL_TYPE_CARDINAL),
-  ordinalProvider(&fLocale, UPLURAL_TYPE_ORDINAL)
+  pluralProvider(*this, UPLURAL_TYPE_CARDINAL),
+  ordinalProvider(*this, UPLURAL_TYPE_ORDINAL)
 {
     // This will take care of creating the hash tables (since they are NULL).
     UErrorCode ec = U_ZERO_ERROR;
@@ -402,7 +417,7 @@ MessageFormat::operator==(const Format& rhs) const
     if (count != rhs_count) {
         return FALSE;
     }
-    int32_t idx = 0, rhs_idx = 0, pos = -1, rhs_pos = -1;
+    int32_t idx = 0, rhs_idx = 0, pos = UHASH_FIRST, rhs_pos = UHASH_FIRST;
     for (; idx < count && rhs_idx < rhs_count && U_SUCCESS(ec); ++idx, ++rhs_idx) {
         const UHashElement* cur = uhash_nextElement(customFormatArgStarts, &pos);
         const UHashElement* rhs_cur = uhash_nextElement(that.customFormatArgStarts, &rhs_pos);
@@ -421,7 +436,7 @@ MessageFormat::operator==(const Format& rhs) const
 // -------------------------------------
 // Creates a copy of this MessageFormat, the caller owns the copy.
 
-Format*
+MessageFormat*
 MessageFormat::clone() const
 {
     return new MessageFormat(*this);
@@ -440,8 +455,8 @@ MessageFormat::setLocale(const Locale& theLocale)
         defaultDateFormat = NULL;
         fLocale = theLocale;
         setLocaleIDs(fLocale.getName(), fLocale.getName());
-        pluralProvider.reset(&fLocale);
-        ordinalProvider.reset(&fLocale);
+        pluralProvider.reset();
+        ordinalProvider.reset();
     }
 }
 
@@ -537,6 +552,7 @@ void MessageFormat::setArgStartFormat(int32_t argStart,
                                       UErrorCode& status) {
     if (U_FAILURE(status)) {
         delete formatter;
+        return;
     }
     if (cachedFormatters == NULL) {
         cachedFormatters=uhash_open(uhash_hashLong, uhash_compareLong,
@@ -774,16 +790,12 @@ MessageFormat::setFormat(const UnicodeString& formatName,
         (partIndex = nextTopLevelArgStart(partIndex)) >= 0 && U_SUCCESS(status);
     ) {
         if (argNameMatches(partIndex + 1, formatName, argNumber)) {
-            if (&newFormat == NULL) {
-                setCustomArgStartFormat(partIndex, NULL, status);
-            } else {
-                Format* new_format = newFormat.clone();
-                if (new_format == NULL) {
-                    status = U_MEMORY_ALLOCATION_ERROR;
-                    return;
-                }
-                setCustomArgStartFormat(partIndex, new_format, status);
+            Format* new_format = newFormat.clone();
+            if (new_format == NULL) {
+                status = U_MEMORY_ALLOCATION_ERROR;
+                return;
             }
+            setCustomArgStartFormat(partIndex, new_format, status);
         }
     }
 }
@@ -798,26 +810,31 @@ MessageFormat::getFormats(int32_t& cnt) const
     // method on this object.  We construct and resize an array
     // on demand that contains aliases to the subformats[i].format
     // pointers.
+
+    // Get total required capacity first (it's refreshed on each call).
+    int32_t totalCapacity = 0;
+    for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0; ++totalCapacity) {}
+
     MessageFormat* t = const_cast<MessageFormat*> (this);
     cnt = 0;
-    if (formatAliases == NULL) {
-        t->formatAliasesCapacity = (argTypeCount<10) ? 10 : argTypeCount;
+    if (formatAliases == nullptr) {
+        t->formatAliasesCapacity = totalCapacity;
         Format** a = (Format**)
             uprv_malloc(sizeof(Format*) * formatAliasesCapacity);
-        if (a == NULL) {
+        if (a == nullptr) {
             t->formatAliasesCapacity = 0;
-            return NULL;
+            return nullptr;
         }
         t->formatAliases = a;
-    } else if (argTypeCount > formatAliasesCapacity) {
+    } else if (totalCapacity > formatAliasesCapacity) {
         Format** a = (Format**)
-            uprv_realloc(formatAliases, sizeof(Format*) * argTypeCount);
-        if (a == NULL) {
+            uprv_realloc(formatAliases, sizeof(Format*) * totalCapacity);
+        if (a == nullptr) {
             t->formatAliasesCapacity = 0;
-            return NULL;
+            return nullptr;
         }
         t->formatAliases = a;
-        t->formatAliasesCapacity = argTypeCount;
+        t->formatAliasesCapacity = totalCapacity;
     }
 
     for (int32_t partIndex = 0; (partIndex = nextTopLevelArgStart(partIndex)) >= 0;) {
@@ -830,12 +847,7 @@ MessageFormat::getFormats(int32_t& cnt) const
 
 UnicodeString MessageFormat::getArgName(int32_t partIndex) {
     const MessagePattern::Part& part = msgPattern.getPart(partIndex);
-    if (part.getType() == UMSGPAT_PART_TYPE_ARG_NAME) {
-        return msgPattern.getSubstring(part);
-    } else {
-        UnicodeString temp;
-        return itos(part.getValue(), temp);
-    }
+    return msgPattern.getSubstring(part);
 }
 
 StringEnumeration*
@@ -944,13 +956,55 @@ MessageFormat::format(const Formattable* arguments,
 
     UnicodeStringAppendable usapp(appendTo);
     AppendableWrapper app(usapp);
-    format(0, 0.0, arguments, argumentNames, cnt, app, pos, status);
+    format(0, NULL, arguments, argumentNames, cnt, app, pos, status);
     return appendTo;
 }
 
+namespace {
+
+/**
+ * Mutable input/output values for the PluralSelectorProvider.
+ * Separate so that it is possible to make MessageFormat Freezable.
+ */
+class PluralSelectorContext {
+public:
+    PluralSelectorContext(int32_t start, const UnicodeString &name,
+                          const Formattable &num, double off, UErrorCode &errorCode)
+            : startIndex(start), argName(name), offset(off),
+              numberArgIndex(-1), formatter(NULL), forReplaceNumber(FALSE) {
+        // number needs to be set even when select() is not called.
+        // Keep it as a Number/Formattable:
+        // For format() methods, and to preserve information (e.g., BigDecimal).
+        if(off == 0) {
+            number = num;
+        } else {
+            number = num.getDouble(errorCode) - off;
+        }
+    }
+
+    // Input values for plural selection with decimals.
+    int32_t startIndex;
+    const UnicodeString &argName;
+    /** argument number - plural offset */
+    Formattable number;
+    double offset;
+    // Output values for plural selection with decimals.
+    /** -1 if REPLACE_NUMBER, 0 arg not found, >0 ARG_START index */
+    int32_t numberArgIndex;
+    const Format *formatter;
+    /** formatted argument number - plural offset */
+    UnicodeString numberString;
+    /** TRUE if number-offset was formatted with the stock number formatter */
+    UBool forReplaceNumber;
+};
+
+}  // namespace
+
 // if argumentNames is NULL, this means arguments is a numeric array.
 // arguments can not be NULL.
-void MessageFormat::format(int32_t msgStart, double pluralNumber,
+// We use const void *plNumber rather than const PluralSelectorContext *pluralNumber
+// so that we need not declare the PluralSelectorContext in the public header file.
+void MessageFormat::format(int32_t msgStart, const void *plNumber,
                            const Formattable* arguments,
                            const UnicodeString *argumentNames,
                            int32_t cnt,
@@ -973,8 +1027,16 @@ void MessageFormat::format(int32_t msgStart, double pluralNumber,
         }
         prevIndex = part->getLimit();
         if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) {
-            const NumberFormat* nf = getDefaultNumberFormat(success);
-            appendTo.formatAndAppend(nf, Formattable(pluralNumber), success);
+            const PluralSelectorContext &pluralNumber =
+                *static_cast<const PluralSelectorContext *>(plNumber);
+            if(pluralNumber.forReplaceNumber) {
+                // number-offset was already formatted.
+                appendTo.formatAndAppend(pluralNumber.formatter,
+                        pluralNumber.number, pluralNumber.numberString, success);
+            } else {
+                const NumberFormat* nf = getDefaultNumberFormat(success);
+                appendTo.formatAndAppend(nf, pluralNumber.number, success);
+            }
             continue;
         }
         if (type != UMSGPAT_PART_TYPE_ARG_START) {
@@ -984,39 +1046,44 @@ void MessageFormat::format(int32_t msgStart, double pluralNumber,
         UMessagePatternArgType argType = part->getArgType();
         part = &msgPattern.getPart(++i);
         const Formattable* arg;
-        UnicodeString noArg;
+        UBool noArg = FALSE;
+        UnicodeString argName = msgPattern.getSubstring(*part);
         if (argumentNames == NULL) {
             int32_t argNumber = part->getValue();  // ARG_NUMBER
             if (0 <= argNumber && argNumber < cnt) {
                 arg = arguments + argNumber;
             } else {
                 arg = NULL;
-                noArg.append(LEFT_CURLY_BRACE);
-                itos(argNumber, noArg);
-                noArg.append(RIGHT_CURLY_BRACE);
+                noArg = TRUE;
             }
         } else {
-            UnicodeString key;
-            if (part->getType() == UMSGPAT_PART_TYPE_ARG_NAME) {
-                key = msgPattern.getSubstring(*part);
-            } else /* UMSGPAT_PART_TYPE_ARG_NUMBER */ {
-                itos(part->getValue(), key);
-            }
-            arg = getArgFromListByName(arguments, argumentNames, cnt, key);
+            arg = getArgFromListByName(arguments, argumentNames, cnt, argName);
             if (arg == NULL) {
-                noArg.append(LEFT_CURLY_BRACE);
-                noArg.append(key);
-                noArg.append(RIGHT_CURLY_BRACE);
+                noArg = TRUE;
             }
         }
         ++i;
         int32_t prevDestLength = appendTo.length();
         const Format* formatter = NULL;
-        if (!noArg.isEmpty()) {
-            appendTo.append(noArg);
+        if (noArg) {
+            appendTo.append(
+                UnicodeString(LEFT_CURLY_BRACE).append(argName).append(RIGHT_CURLY_BRACE));
         } else if (arg == NULL) {
             appendTo.append(NULL_STRING, 4);
-        } else if ((formatter = getCachedFormatter(i -2))) {
+        } else if(plNumber!=NULL &&
+                static_cast<const PluralSelectorContext *>(plNumber)->numberArgIndex==(i-2)) {
+            const PluralSelectorContext &pluralNumber =
+                *static_cast<const PluralSelectorContext *>(plNumber);
+            if(pluralNumber.offset == 0) {
+                // The number was already formatted with this formatter.
+                appendTo.formatAndAppend(pluralNumber.formatter, pluralNumber.number,
+                                         pluralNumber.numberString, success);
+            } else {
+                // Do not use the formatted (number-offset) string for a named argument
+                // that formats the number without subtracting the offset.
+                appendTo.formatAndAppend(pluralNumber.formatter, *arg, success);
+            }
+        } else if ((formatter = getCachedFormatter(i -2)) != 0) {
             // Handles all ArgType.SIMPLE, and formatters from setFormat() and its siblings.
             if (dynamic_cast<const ChoiceFormat*>(formatter) ||
                 dynamic_cast<const PluralFormat*>(formatter) ||
@@ -1030,7 +1097,7 @@ void MessageFormat::format(int32_t msgStart, double pluralNumber,
                     (subMsgString.indexOf(SINGLE_QUOTE) >= 0 && !MessageImpl::jdkAposMode(msgPattern))
                 ) {
                     MessageFormat subMsgFormat(subMsgString, fLocale, success);
-                    subMsgFormat.format(0, 0, arguments, argumentNames, cnt, appendTo, ignore, success);
+                    subMsgFormat.format(0, NULL, arguments, argumentNames, cnt, appendTo, ignore, success);
                 } else {
                     appendTo.append(subMsgString);
                 }
@@ -1059,26 +1126,26 @@ void MessageFormat::format(int32_t msgStart, double pluralNumber,
             // because only this one converts non-double numeric types to double.
             const double number = arg->getDouble(success);
             int32_t subMsgStart = ChoiceFormat::findSubMessage(msgPattern, i, number);
-            formatComplexSubMessage(subMsgStart, 0, arguments, argumentNames,
+            formatComplexSubMessage(subMsgStart, NULL, arguments, argumentNames,
                                     cnt, appendTo, success);
         } else if (UMSGPAT_ARG_TYPE_HAS_PLURAL_STYLE(argType)) {
             if (!arg->isNumeric()) {
                 success = U_ILLEGAL_ARGUMENT_ERROR;
                 return;
             }
-            const PluralFormat::PluralSelector &selector =
+            const PluralSelectorProvider &selector =
                 argType == UMSGPAT_ARG_TYPE_PLURAL ? pluralProvider : ordinalProvider;
             // We must use the Formattable::getDouble() variant with the UErrorCode parameter
             // because only this one converts non-double numeric types to double.
-            double number = arg->getDouble(success);
-            int32_t subMsgStart = PluralFormat::findSubMessage(msgPattern, i, selector, number,
-                                                               success);
             double offset = msgPattern.getPluralOffset(i);
-            formatComplexSubMessage(subMsgStart, number-offset, arguments, argumentNames,
+            PluralSelectorContext context(i, argName, *arg, offset, success);
+            int32_t subMsgStart = PluralFormat::findSubMessage(
+                    msgPattern, i, selector, &context, arg->getDouble(success), success);
+            formatComplexSubMessage(subMsgStart, &context, arguments, argumentNames,
                                     cnt, appendTo, success);
         } else if (argType == UMSGPAT_ARG_TYPE_SELECT) {
             int32_t subMsgStart = SelectFormat::findSubMessage(msgPattern, i, arg->getString(success), success);
-            formatComplexSubMessage(subMsgStart, 0, arguments, argumentNames,
+            formatComplexSubMessage(subMsgStart, NULL, arguments, argumentNames,
                                     cnt, appendTo, success);
         } else {
             // This should never happen.
@@ -1093,7 +1160,7 @@ void MessageFormat::format(int32_t msgStart, double pluralNumber,
 
 
 void MessageFormat::formatComplexSubMessage(int32_t msgStart,
-                                            double pluralNumber,
+                                            const void *plNumber,
                                             const Formattable* arguments,
                                             const UnicodeString *argumentNames,
                                             int32_t cnt,
@@ -1104,7 +1171,7 @@ void MessageFormat::formatComplexSubMessage(int32_t msgStart,
     }
 
     if (!MessageImpl::jdkAposMode(msgPattern)) {
-        format(msgStart, pluralNumber, arguments, argumentNames, cnt, appendTo, NULL, success);
+        format(msgStart, plNumber, arguments, argumentNames, cnt, appendTo, NULL, success);
         return;
     }
 
@@ -1126,8 +1193,15 @@ void MessageFormat::formatComplexSubMessage(int32_t msgStart,
         } else if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER || type == UMSGPAT_PART_TYPE_SKIP_SYNTAX) {
             sb.append(msgString, prevIndex, index - prevIndex);
             if (type == UMSGPAT_PART_TYPE_REPLACE_NUMBER) {
-                const NumberFormat* nf = getDefaultNumberFormat(success);
-                sb.append(nf->format(pluralNumber, sb, success));
+                const PluralSelectorContext &pluralNumber =
+                    *static_cast<const PluralSelectorContext *>(plNumber);
+                if(pluralNumber.forReplaceNumber) {
+                    // number-offset was already formatted.
+                    sb.append(pluralNumber.numberString);
+                } else {
+                    const NumberFormat* nf = getDefaultNumberFormat(success);
+                    sb.append(nf->format(pluralNumber.number, sb, success));
+                }
             }
             prevIndex = part.getLimit();
         } else if (type == UMSGPAT_PART_TYPE_ARG_START) {
@@ -1143,7 +1217,7 @@ void MessageFormat::formatComplexSubMessage(int32_t msgStart,
         UnicodeString emptyPattern;  // gcc 3.3.3 fails with "UnicodeString()" as the first parameter.
         MessageFormat subMsgFormat(emptyPattern, fLocale, success);
         subMsgFormat.applyPattern(sb, UMSGPAT_APOS_DOUBLE_REQUIRED, NULL, success);
-        subMsgFormat.format(0, 0, arguments, argumentNames, cnt, appendTo, NULL, success);
+        subMsgFormat.format(0, NULL, arguments, argumentNames, cnt, appendTo, NULL, success);
     } else {
         appendTo.append(sb);
     }
@@ -1183,6 +1257,59 @@ FieldPosition* MessageFormat::updateMetaData(AppendableWrapper& /*dest*/, int32_
     */
 }
 
+int32_t
+MessageFormat::findOtherSubMessage(int32_t partIndex) const {
+    int32_t count=msgPattern.countParts();
+    const MessagePattern::Part *part = &msgPattern.getPart(partIndex);
+    if(MessagePattern::Part::hasNumericValue(part->getType())) {
+        ++partIndex;
+    }
+    // Iterate over (ARG_SELECTOR [ARG_INT|ARG_DOUBLE] message) tuples
+    // until ARG_LIMIT or end of plural-only pattern.
+    UnicodeString other(FALSE, OTHER_STRING, 5);
+    do {
+        part=&msgPattern.getPart(partIndex++);
+        UMessagePatternPartType type=part->getType();
+        if(type==UMSGPAT_PART_TYPE_ARG_LIMIT) {
+            break;
+        }
+        U_ASSERT(type==UMSGPAT_PART_TYPE_ARG_SELECTOR);
+        // part is an ARG_SELECTOR followed by an optional explicit value, and then a message
+        if(msgPattern.partSubstringMatches(*part, other)) {
+            return partIndex;
+        }
+        if(MessagePattern::Part::hasNumericValue(msgPattern.getPartType(partIndex))) {
+            ++partIndex;  // skip the numeric-value part of "=1" etc.
+        }
+        partIndex=msgPattern.getLimitPartIndex(partIndex);
+    } while(++partIndex<count);
+    return 0;
+}
+
+int32_t
+MessageFormat::findFirstPluralNumberArg(int32_t msgStart, const UnicodeString &argName) const {
+    for(int32_t i=msgStart+1;; ++i) {
+        const MessagePattern::Part &part=msgPattern.getPart(i);
+        UMessagePatternPartType type=part.getType();
+        if(type==UMSGPAT_PART_TYPE_MSG_LIMIT) {
+            return 0;
+        }
+        if(type==UMSGPAT_PART_TYPE_REPLACE_NUMBER) {
+            return -1;
+        }
+        if(type==UMSGPAT_PART_TYPE_ARG_START) {
+            UMessagePatternArgType argType=part.getArgType();
+            if(!argName.isEmpty() && (argType==UMSGPAT_ARG_TYPE_NONE || argType==UMSGPAT_ARG_TYPE_SIMPLE)) {
+                // ARG_NUMBER or ARG_NAME
+                if(msgPattern.partSubstringMatches(msgPattern.getPart(i+1), argName)) {
+                    return i;
+                }
+            }
+            i=msgPattern.getLimitPartIndex(i);
+        }
+    }
+}
+
 void MessageFormat::copyObjects(const MessageFormat& that, UErrorCode& ec) {
     // Deep copy pointer fields.
     // We need not copy the formatAliases because they are re-filled
@@ -1214,7 +1341,7 @@ void MessageFormat::copyObjects(const MessageFormat& that, UErrorCode& ec) {
 
         const int32_t count = uhash_count(that.cachedFormatters);
         int32_t pos, idx;
-        for (idx = 0, pos = -1; idx < count && U_SUCCESS(ec); ++idx) {
+        for (idx = 0, pos = UHASH_FIRST; idx < count && U_SUCCESS(ec); ++idx) {
             const UHashElement* cur = uhash_nextElement(that.cachedFormatters, &pos);
             Format* newFormat = ((Format*)(cur->value.pointer))->clone();
             if (newFormat) {
@@ -1232,7 +1359,7 @@ void MessageFormat::copyObjects(const MessageFormat& that, UErrorCode& ec) {
         }
         const int32_t count = uhash_count(that.customFormatArgStarts);
         int32_t pos, idx;
-        for (idx = 0, pos = -1; idx < count && U_SUCCESS(ec); ++idx) {
+        for (idx = 0, pos = UHASH_FIRST; idx < count && U_SUCCESS(ec); ++idx) {
             const UHashElement* cur = uhash_nextElement(that.customFormatArgStarts, &pos);
             uhash_iputi(customFormatArgStarts, cur->key.integer, cur->value.integer, &ec);
         }
@@ -1551,7 +1678,6 @@ void MessageFormat::cacheExplicitFormats(UErrorCode& status) {
     }
 }
 
-
 Format* MessageFormat::createAppropriateFormat(UnicodeString& type, UnicodeString& style,
                                                Formattable::Type& formattableType, UParseError& parseError,
                                                UErrorCode& ec) {
@@ -1561,6 +1687,7 @@ Format* MessageFormat::createAppropriateFormat(UnicodeString& type, UnicodeStrin
     Format* fmt = NULL;
     int32_t typeID, styleID;
     DateFormat::EStyle date_style;
+    int32_t firstNonSpace;
 
     switch (typeID = findKeyword(type, TYPE_IDS)) {
     case 0: // number
@@ -1579,12 +1706,20 @@ Format* MessageFormat::createAppropriateFormat(UnicodeString& type, UnicodeStrin
             formattableType = Formattable::kLong;
             fmt = createIntegerFormat(fLocale, ec);
             break;
-        default: // pattern
-            fmt = NumberFormat::createInstance(fLocale, ec);
-            if (fmt) {
-                DecimalFormat* decfmt = dynamic_cast<DecimalFormat*>(fmt);
-                if (decfmt != NULL) {
-                    decfmt->applyPattern(style,parseError,ec);
+        default: // pattern or skeleton
+            firstNonSpace = PatternProps::skipWhiteSpace(style, 0);
+            if (style.compare(firstNonSpace, 2, u"::", 0, 2) == 0) {
+                // Skeleton
+                UnicodeString skeleton = style.tempSubString(firstNonSpace + 2);
+                fmt = number::NumberFormatter::forSkeleton(skeleton, ec).locale(fLocale).toFormat(ec);
+            } else {
+                // Pattern
+                fmt = NumberFormat::createInstance(fLocale, ec);
+                if (fmt) {
+                    auto* decfmt = dynamic_cast<DecimalFormat*>(fmt);
+                    if (decfmt != nullptr) {
+                        decfmt->applyPattern(style, parseError, ec);
+                    }
                 }
             }
             break;
@@ -1594,19 +1729,27 @@ Format* MessageFormat::createAppropriateFormat(UnicodeString& type, UnicodeStrin
     case 1: // date
     case 2: // time
         formattableType = Formattable::kDate;
-        styleID = findKeyword(style, DATE_STYLE_IDS);
-        date_style = (styleID >= 0) ? DATE_STYLES[styleID] : DateFormat::kDefault;
-
-        if (typeID == 1) {
-            fmt = DateFormat::createDateInstance(date_style, fLocale);
+        firstNonSpace = PatternProps::skipWhiteSpace(style, 0);
+        if (style.compare(firstNonSpace, 2, u"::", 0, 2) == 0) {
+            // Skeleton
+            UnicodeString skeleton = style.tempSubString(firstNonSpace + 2);
+            fmt = DateFormat::createInstanceForSkeleton(skeleton, fLocale, ec);
         } else {
-            fmt = DateFormat::createTimeInstance(date_style, fLocale);
-        }
+            // Pattern
+            styleID = findKeyword(style, DATE_STYLE_IDS);
+            date_style = (styleID >= 0) ? DATE_STYLES[styleID] : DateFormat::kDefault;
+
+            if (typeID == 1) {
+                fmt = DateFormat::createDateInstance(date_style, fLocale);
+            } else {
+                fmt = DateFormat::createTimeInstance(date_style, fLocale);
+            }
 
-        if (styleID < 0 && fmt != NULL) {
-            SimpleDateFormat* sdtfmt = dynamic_cast<SimpleDateFormat*>(fmt);
-            if (sdtfmt != NULL) {
-                sdtfmt->applyPattern(style);
+            if (styleID < 0 && fmt != NULL) {
+                SimpleDateFormat* sdtfmt = dynamic_cast<SimpleDateFormat*>(fmt);
+                if (sdtfmt != NULL) {
+                    sdtfmt->applyPattern(style);
+                }
             }
         }
         break;
@@ -1730,7 +1873,7 @@ UBool MessageFormat::DummyFormat::operator==(const Format&) const {
     return TRUE;
 }
 
-Format* MessageFormat::DummyFormat::clone() const {
+MessageFormat::DummyFormat* MessageFormat::DummyFormat::clone() const {
     return new DummyFormat();
 }
 
@@ -1796,32 +1939,62 @@ FormatNameEnumeration::~FormatNameEnumeration() {
     delete fFormatNames;
 }
 
-
-MessageFormat::PluralSelectorProvider::PluralSelectorProvider(const Locale* loc, UPluralType t)
-        : locale(loc), rules(NULL), type(t) {
+MessageFormat::PluralSelectorProvider::PluralSelectorProvider(const MessageFormat &mf, UPluralType t)
+        : msgFormat(mf), rules(NULL), type(t) {
 }
 
 MessageFormat::PluralSelectorProvider::~PluralSelectorProvider() {
-    // We own the rules but not the locale.
     delete rules;
 }
 
-UnicodeString MessageFormat::PluralSelectorProvider::select(double number, UErrorCode& ec) const {
+UnicodeString MessageFormat::PluralSelectorProvider::select(void *ctx, double number,
+                                                            UErrorCode& ec) const {
     if (U_FAILURE(ec)) {
         return UnicodeString(FALSE, OTHER_STRING, 5);
     }
     MessageFormat::PluralSelectorProvider* t = const_cast<MessageFormat::PluralSelectorProvider*>(this);
     if(rules == NULL) {
-        t->rules = PluralRules::forLocale(*locale, type, ec);
+        t->rules = PluralRules::forLocale(msgFormat.fLocale, type, ec);
         if (U_FAILURE(ec)) {
             return UnicodeString(FALSE, OTHER_STRING, 5);
         }
     }
-    return rules->select(number);
+    // Select a sub-message according to how the number is formatted,
+    // which is specified in the selected sub-message.
+    // We avoid this circle by looking at how
+    // the number is formatted in the "other" sub-message
+    // which must always be present and usually contains the number.
+    // Message authors should be consistent across sub-messages.
+    PluralSelectorContext &context = *static_cast<PluralSelectorContext *>(ctx);
+    int32_t otherIndex = msgFormat.findOtherSubMessage(context.startIndex);
+    context.numberArgIndex = msgFormat.findFirstPluralNumberArg(otherIndex, context.argName);
+    if(context.numberArgIndex > 0 && msgFormat.cachedFormatters != NULL) {
+        context.formatter =
+            (const Format*)uhash_iget(msgFormat.cachedFormatters, context.numberArgIndex);
+    }
+    if(context.formatter == NULL) {
+        context.formatter = msgFormat.getDefaultNumberFormat(ec);
+        context.forReplaceNumber = TRUE;
+    }
+    if (context.number.getDouble(ec) != number) {
+        ec = U_INTERNAL_PROGRAM_ERROR;
+        return UnicodeString(FALSE, OTHER_STRING, 5);
+    }
+    context.formatter->format(context.number, context.numberString, ec);
+    auto* decFmt = dynamic_cast<const DecimalFormat *>(context.formatter);
+    if(decFmt != NULL) {
+        number::impl::DecimalQuantity dq;
+        decFmt->formatToDecimalQuantity(context.number, dq, ec);
+        if (U_FAILURE(ec)) {
+            return UnicodeString(FALSE, OTHER_STRING, 5);
+        }
+        return rules->select(dq);
+    } else {
+        return rules->select(number);
+    }
 }
 
-void MessageFormat::PluralSelectorProvider::reset(const Locale* loc) {
-    locale = loc;
+void MessageFormat::PluralSelectorProvider::reset() {
     delete rules;
     rules = NULL;
 }