]> granicus.if.org Git - icu/commitdiff
ICU-11276 Adding initial header files for NumberRangeFormatter.
authorShane Carr <shane@unicode.org>
Wed, 29 Aug 2018 05:34:38 +0000 (22:34 -0700)
committerShane Carr <shane@unicode.org>
Thu, 27 Sep 2018 21:27:39 +0000 (14:27 -0700)
icu4c/source/i18n/numrange_types.h [new file with mode: 0644]
icu4c/source/i18n/unicode/numberrangeformatter.h [new file with mode: 0644]

diff --git a/icu4c/source/i18n/numrange_types.h b/icu4c/source/i18n/numrange_types.h
new file mode 100644 (file)
index 0000000..77b0ea6
--- /dev/null
@@ -0,0 +1,50 @@
+// © 2018 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+#ifndef __SOURCE_NUMRANGE_TYPES_H__
+#define __SOURCE_NUMRANGE_TYPES_H__
+
+#include "unicode/numberformatter.h"
+#include "unicode/numberrangeformatter.h"
+#include "number_types.h"
+#include "number_decimalquantity.h"
+#include "number_stringbuilder.h"
+
+U_NAMESPACE_BEGIN namespace number {
+namespace impl {
+
+
+/**
+ * Implementation class for UFormattedNumber with magic number for safety.
+ *
+ * This struct is also held internally by the C++ version FormattedNumber since the member types are not
+ * declared in the public header file.
+ *
+ * The DecimalQuantity is not currently being used by FormattedNumber, but at some point it could be used
+ * to add a toDecNumber() or similar method.
+ */
+struct UFormattedNumberRangeData : public UMemory {
+    // The magic number to identify incoming objects.
+    // Reads in ASCII as "FDR" (FormatteDnumberRange with room at the end)
+    static constexpr int32_t kMagic = 0x46445200;
+
+    // Data members:
+    int32_t fMagic = kMagic;
+    DecimalQuantity quantity1;
+    DecimalQuantity quantity2;
+    NumberStringBuilder string;
+    UNumberRangeIdentityType identityType;
+
+    // No C conversion methods (no C API yet)
+};
+
+
+} // namespace impl
+} // namespace number
+U_NAMESPACE_END
+
+#endif //__SOURCE_NUMRANGE_TYPES_H__
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/unicode/numberrangeformatter.h b/icu4c/source/i18n/unicode/numberrangeformatter.h
new file mode 100644 (file)
index 0000000..d4e1e6b
--- /dev/null
@@ -0,0 +1,826 @@
+// © 2018 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#if !UCONFIG_NO_FORMATTING
+#ifndef __NUMBERRANGEFORMATTER_H__
+#define __NUMBERRANGEFORMATTER_H__
+
+#include "unicode/appendable.h"
+#include "unicode/fieldpos.h"
+#include "unicode/fpositer.h"
+#include "unicode/numberformatter.h"
+
+#ifndef U_HIDE_DRAFT_API
+
+/**
+ * \file
+ * \brief C++ API: Library for localized formatting of number, currency, and unit ranges.
+ *
+ * The main entrypoint to the formatting of ranges of numbers, including currencies and other units of measurement.
+ * <p>
+ * Usage example:
+ * <p>
+ * <pre>
+ * NumberRangeFormatter::with()
+ *     .identityFallback(UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE)
+ *     .numberFormatterFirst(NumberFormatter::with().adoptUnit(MeasureUnit::createMeter()))
+ *     .numberFormatterSecond(NumberFormatter::with().adoptUnit(MeasureUnit::createKilometer()))
+ *     .locale("en-GB")
+ *     .formatRange(750, 1.2, status)
+ *     .toString(status);
+ * // => "750 m - 1.2 km"
+ * </pre>
+ * <p>
+ * Like NumberFormatter, NumberRangeFormatter instances are immutable and thread-safe. This API is based on the
+ * <em>fluent</em> design pattern popularized by libraries such as Google's Guava.
+ *
+ * <p>
+ * This API is based on the <em>fluent</em> design pattern popularized by libraries such as Google's Guava. For
+ * extensive details on the design of this API, read <a href="https://goo.gl/szi5VB">the design doc</a>.
+ *
+ * @author Shane Carr
+ */
+
+
+/**
+ * Defines how to merge fields that are identical across the range sign.
+ *
+ * @draft ICU 63
+ */
+typedef enum UNumberRangeCollapse {
+    /**
+     * Use locale data and heuristics to determine how much of the string to collapse. Could end up collapsing none,
+     * some, or all repeated pieces in a locale-sensitive way.
+     *
+     * @draft ICU 63
+     */
+    UNUM_RANGE_COLLAPSE_AUTO,
+
+    /**
+     * Do not collapse any part of the number. Example: "3.2 thousand kilograms – 5.3 thousand kilograms"
+     *
+     * @draft ICU 63
+     */
+    UNUM_RANGE_COLLAPSE_NONE,
+
+    /**
+     * Collapse the unit part of the number, but not the notation, if present. Example: "3.2 thousand – 5.3 thousand
+     * kilograms"
+     *
+     * @draft ICU 63
+     */
+    UNUM_RANGE_COLLAPSE_UNIT,
+
+    /**
+     * Collapse any field that is equal across the range sign. May introduce ambiguity on the magnitude of the
+     * number. Example: "3.2 – 5.3 thousand kilograms"
+     *
+     * @draft ICU 63
+     */
+    UNUM_RANGE_COLLAPSE_ALL
+} UNumberRangeCollapse;
+
+/**
+ * Defines the behavior when the two numbers in the range are identical after rounding. To programmatically detect
+ * when the identity fallback is used, compare the lower and upper BigDecimals via FormattedNumber.
+ *
+ * @draft ICU 63
+ * @provisional This API might change or be removed in a future release.
+ * @see NumberRangeFormatter
+ */
+typedef enum UNumberRangeIdentityFallback {
+    /**
+     * Show the number as a single value rather than a range. Example: "$5"
+     *
+     * @draft ICU 63
+     */
+    UNUM_IDENTITY_FALLBACK_SINGLE_VALUE,
+
+    /**
+     * Show the number using a locale-sensitive approximation pattern. If the numbers were the same before rounding,
+     * show the single value. Example: "~$5" or "$5"
+     *
+     * @draft ICU 63
+     */
+    UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE,
+
+    /**
+     * Show the number using a locale-sensitive approximation pattern. Use the range pattern always, even if the
+     * inputs are the same. Example: "~$5"
+     *
+     * @draft ICU 63
+     */
+    UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
+
+    /**
+     * Show the number as the range of two equal values. Use the range pattern always, even if the inputs are the
+     * same. Example (with RangeCollapse.NONE): "$5 – $5"
+     *
+     * @draft ICU 63
+     */
+    UNUM_IDENTITY_FALLBACK_RANGE
+} UNumberIdentityFallback;
+
+/**
+ * Used in the result class FormattedNumberRange to indicate to the user whether the numbers formatted in the range
+ * were equal or not, and whether or not the identity fallback was applied.
+ *
+ * @draft ICU 63
+ * @provisional This API might change or be removed in a future release.
+ * @see NumberRangeFormatter
+ */
+typedef enum UNumberRangeIdentityType {
+    /**
+     * Used to indicate that the two numbers in the range were equal, even before any rounding rules were applied.
+     *
+     * @draft ICU 63
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberRangeFormatter
+     */
+    UNUM_IDENTITY_TYPE_EQUAL_BEFORE_ROUNDING,
+
+    /**
+     * Used to indicate that the two numbers in the range were equal, but only after rounding rules were applied.
+     *
+     * @draft ICU 63
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberRangeFormatter
+     */
+    UNUM_IDENTITY_TYPE_EQUAL_AFTER_ROUNDING,
+
+    /**
+     * Used to indicate that the two numbers in the range were not equal, even after rounding rules were applied.
+     *
+     * @draft ICU 63
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberRangeFormatter
+     */
+    UNUM_IDENTITY_TYPE_NOT_EQUAL
+} UNumberRangeIdentityType;
+
+U_NAMESPACE_BEGIN
+
+namespace number {  // icu::number
+
+namespace impl {  // icu::number::impl
+
+// Do not enclose entire MacroProps with #ifndef U_HIDE_INTERNAL_API, needed for a protected field
+/** @internal */
+struct U_I18N_API RangeMacroProps : public UMemory {
+    /** @internal */
+    UnlocalizedNumberFormatter formatter1; // = NumberFormatter::with();
+
+    /** @internal */
+    UnlocalizedNumberFormatter formatter2; // = NumberFormatter::with();
+
+    /** @internal */
+    UNumberRangeCollapse collapse = UNUM_RANGE_COLLAPSE_AUTO;
+
+    /** @internal */
+    UNumberIdentityFallback identityFallback = UNUM_IDENTITY_APPROXIMATELY;
+
+    /** @internal */
+    Locale locale;
+
+    // NOTE: Uses default copy and move constructors.
+
+    /**
+     * Check all members for errors.
+     * @internal
+     */
+    bool copyErrorTo(UErrorCode &status) const {
+        return formatter1.copyErrorTo(status) || formatter2.copyErrorTo(status);
+    }
+};
+
+} // namespace impl
+
+/**
+ * An abstract base class for specifying settings related to number formatting. This class is implemented by
+ * {@link UnlocalizedNumberRangeFormatter} and {@link LocalizedNumberRangeFormatter}. This class is not intended for
+ * public subclassing.
+ */
+template<typename Derived>
+class U_I18N_API NumberRangeFormatterSettings {
+  public:
+    /**
+     * Sets the NumberFormatter instance to use for the numbers in the range. The same formatter is applied to both
+     * sides of the range.
+     * <p>
+     * The NumberFormatter instances must not have a locale applied yet; the locale specified on the
+     * NumberRangeFormatter will be used.
+     *
+     * @param formatter
+     *            The formatter to use for both numbers in the range.
+     * @return The fluent chain.
+     * @draft ICU 63
+     */
+    Derived numberFormatterBoth(const UnlocalizedNumberFormatter &formatter) const &;
+
+    /**
+     * Overload of numberFormatterBoth() for use on an rvalue reference.
+     *
+     * @param formatter
+     *            The formatter to use for both numbers in the range.
+     * @return The fluent chain.
+     * @see #numberFormatterBoth
+     * @draft ICU 63
+     */
+    Derived numberFormatterBoth(const UnlocalizedNumberFormatter &formatter) &&;
+
+    /**
+     * Overload of numberFormatterBoth() for use on an rvalue reference.
+     *
+     * @param formatter
+     *            The formatter to use for both numbers in the range.
+     * @return The fluent chain.
+     * @see #numberFormatterBoth
+     * @draft ICU 63
+     */
+    Derived numberFormatterBoth(UnlocalizedNumberFormatter &&formatter) const &;
+
+    /**
+     * Overload of numberFormatterBoth() for use on an rvalue reference.
+     *
+     * @param formatter
+     *            The formatter to use for both numbers in the range.
+     * @return The fluent chain.
+     * @see #numberFormatterBoth
+     * @draft ICU 63
+     */
+    Derived numberFormatterBoth(UnlocalizedNumberFormatter &&formatter) &&;
+
+    /**
+     * Sets the NumberFormatter instance to use for the first number in the range.
+     * <p>
+     * The NumberFormatter instances must not have a locale applied yet; the locale specified on the
+     * NumberRangeFormatter will be used.
+     *
+     * @param formatterFirst
+     *            The formatter to use for the first number in the range.
+     * @return The fluent chain.
+     * @draft ICU 63
+     */
+    Derived numberFormatterFirst(const UnlocalizedNumberFormatter &formatterFirst) const &;
+
+    /**
+     * Overload of numberFormatterFirst() for use on an rvalue reference.
+     *
+     * @param formatterFirst
+     *            The formatter to use for both numbers in the range.
+     * @return The fluent chain.
+     * @see #numberFormatterFirst
+     * @draft ICU 63
+     */
+    Derived numberFormatterFirst(const UnlocalizedNumberFormatter &formatterFirst) &&;
+
+    /**
+     * Overload of numberFormatterFirst() for use on an rvalue reference.
+     *
+     * @param formatterFirst
+     *            The formatter to use for both numbers in the range.
+     * @return The fluent chain.
+     * @see #numberFormatterFirst
+     * @draft ICU 63
+     */
+    Derived numberFormatterFirst(UnlocalizedNumberFormatter &&formatterFirst) const &;
+
+    /**
+     * Overload of numberFormatterFirst() for use on an rvalue reference.
+     *
+     * @param formatterFirst
+     *            The formatter to use for both numbers in the range.
+     * @return The fluent chain.
+     * @see #numberFormatterFirst
+     * @draft ICU 63
+     */
+    Derived numberFormatterFirst(UnlocalizedNumberFormatter &&formatterFirst) &&;
+
+    /**
+     * Sets the NumberFormatter instance to use for the second number in the range.
+     * <p>
+     * The NumberFormatter instances must not have a locale applied yet; the locale specified on the
+     * NumberRangeFormatter will be used.
+     *
+     * @param formatterSecond
+     *            The formatter to use for the second number in the range.
+     * @return The fluent chain.
+     * @draft ICU 63
+     */
+    Derived numberFormatterSecond(const UnlocalizedNumberFormatter &formatterSecond) const &;
+
+    /**
+     * Overload of numberFormatterSecond() for use on an rvalue reference.
+     *
+     * @param formatterSecond
+     *            The formatter to use for both numbers in the range.
+     * @return The fluent chain.
+     * @see #numberFormatterSecond
+     * @draft ICU 63
+     */
+    Derived numberFormatterSecond(const UnlocalizedNumberFormatter &formatterSecond) &&;
+
+    /**
+     * Overload of numberFormatterSecond() for use on an rvalue reference.
+     *
+     * @param formatterSecond
+     *            The formatter to use for both numbers in the range.
+     * @return The fluent chain.
+     * @see #numberFormatterSecond
+     * @draft ICU 63
+     */
+    Derived numberFormatterSecond(UnlocalizedNumberFormatter &&formatterSecond) const &;
+
+    /**
+     * Overload of numberFormatterSecond() for use on an rvalue reference.
+     *
+     * @param formatterSecond
+     *            The formatter to use for both numbers in the range.
+     * @return The fluent chain.
+     * @see #numberFormatterSecond
+     * @draft ICU 63
+     */
+    Derived numberFormatterSecond(UnlocalizedNumberFormatter &&formatterSecond) &&;
+
+    /**
+     * Sets the aggressiveness of "collapsing" fields across the range separator. Possible values:
+     * <p>
+     * <ul>
+     * <li>ALL: "3-5K miles"</li>
+     * <li>UNIT: "3K - 5K miles"</li>
+     * <li>NONE: "3K miles - 5K miles"</li>
+     * <li>AUTO: usually UNIT or NONE, depending on the locale and formatter settings</li>
+     * <p>
+     * The default value is AUTO.
+     *
+     * @param collapse
+     *            The collapsing strategy to use for this range.
+     * @return The fluent chain.
+     * @draft ICU 63
+     */
+    Derived collapse(UNumberRangeCollapse collapse) const &;
+
+    /**
+     * Overload of collapse() for use on an rvalue reference.
+     *
+     * @param collapse
+     *            The collapsing strategy to use for this range.
+     * @return The fluent chain.
+     * @see #collapse
+     * @draft ICU 63
+     */
+    Derived collapse(UNumberRangeCollapse collapse) &&;
+
+    /**
+     * Sets the behavior when the two sides of the range are the same. This could happen if the same two numbers are
+     * passed to the formatRange function, or if different numbers are passed to the function but they become the same
+     * after rounding rules are applied. Possible values:
+     * <p>
+     * <ul>
+     * <li>SINGLE_VALUE: "5 miles"</li>
+     * <li>APPROXIMATELY_OR_SINGLE_VALUE: "~5 miles" or "5 miles", depending on whether the number was the same before
+     * rounding was applied</li>
+     * <li>APPROXIMATELY: "~5 miles"</li>
+     * <li>RANGE: "5-5 miles" (with collapse=UNIT)</li>
+     * <p>
+     * The default value is APPROXIMATELY.
+     *
+     * @param identityFallback
+     *            The strategy to use when formatting two numbers that end up being the same.
+     * @return The fluent chain.
+     * @draft ICU 63
+     */
+    Derived identityFallback(UNumberIdentityFallback identityFallback) const &;
+
+    /**
+     * Overload of identityFallback() for use on an rvalue reference.
+     *
+     * @param identityFallback
+     *            The strategy to use when formatting two numbers that end up being the same.
+     * @return The fluent chain.
+     * @see #identityFallback
+     * @draft ICU 63
+     */
+    Derived identityFallback(UNumberIdentityFallback identityFallback) &&;
+
+    /**
+     * Sets the UErrorCode if an error occurred in the fluent chain.
+     * Preserves older error codes in the outErrorCode.
+     * @return TRUE if U_FAILURE(outErrorCode)
+     * @draft ICU 63
+     */
+    UBool copyErrorTo(UErrorCode &outErrorCode) const {
+        if (U_FAILURE(outErrorCode)) {
+            // Do not overwrite the older error code
+            return TRUE;
+        }
+        fMacros.copyErrorTo(outErrorCode);
+        return U_FAILURE(outErrorCode);
+    };
+
+    // NOTE: Uses default copy and move constructors.
+
+  protected:
+    impl::RangeMacroProps fMacros;
+
+  private:
+    // Don't construct me directly!  Use (Un)LocalizedNumberFormatter.
+    NumberRangeFormatterSettings() = default;
+
+    friend class LocalizedNumberRangeFormatter;
+    friend class UnlocalizedNumberRangeFormatter;
+};
+
+/**
+ * A NumberRangeFormatter that does not yet have a locale. In order to format, a locale must be specified.
+ *
+ * @see NumberRangeFormatter
+ * @draft ICU 63
+ */
+class U_I18N_API UnlocalizedNumberRangeFormatter
+        : public NumberRangeFormatterSettings<UnlocalizedNumberRangeFormatter>, public UMemory {
+
+  public:
+    /**
+     * Associate the given locale with the number range formatter. The locale is used for picking the
+     * appropriate symbols, formats, and other data for number display.
+     *
+     * @param locale
+     *            The locale to use when loading data for number formatting.
+     * @return The fluent chain.
+     * @draft ICU 63
+     */
+    LocalizedNumberRangeFormatter locale(const icu::Locale &locale) const &;
+
+    /**
+     * Overload of locale() for use on an rvalue reference.
+     *
+     * @param locale
+     *            The locale to use when loading data for number formatting.
+     * @return The fluent chain.
+     * @see #locale
+     * @draft ICU 63
+     */
+    LocalizedNumberRangeFormatter locale(const icu::Locale &locale) &&;
+
+    /**
+     * Default constructor: puts the formatter into a valid but undefined state.
+     *
+     * @draft ICU 63
+     */
+    UnlocalizedNumberRangeFormatter() = default;
+
+    // Make default copy constructor call the NumberFormatterSettings copy constructor.
+    /**
+     * Returns a copy of this UnlocalizedNumberRangeFormatter.
+     * @draft ICU 63
+     */
+    UnlocalizedNumberRangeFormatter(const UnlocalizedNumberRangeFormatter &other);
+
+    /**
+     * Move constructor:
+     * The source UnlocalizedNumberRangeFormatter will be left in a valid but undefined state.
+     * @draft ICU 63
+     */
+    UnlocalizedNumberRangeFormatter(UnlocalizedNumberRangeFormatter&& src) U_NOEXCEPT;
+
+    /**
+     * Copy assignment operator.
+     * @draft ICU 63
+     */
+    UnlocalizedNumberRangeFormatter& operator=(const UnlocalizedNumberRangeFormatter& other);
+
+    /**
+     * Move assignment operator:
+     * The source UnlocalizedNumberRangeFormatter will be left in a valid but undefined state.
+     * @draft ICU 63
+     */
+    UnlocalizedNumberRangeFormatter& operator=(UnlocalizedNumberRangeFormatter&& src) U_NOEXCEPT;
+
+  private:
+    explicit UnlocalizedNumberRangeFormatter(
+            const NumberRangeFormatterSettings<UnlocalizedNumberRangeFormatter>& other);
+
+    explicit UnlocalizedNumberRangeFormatter(
+            NumberRangeFormatterSettings<UnlocalizedNumberRangeFormatter>&& src) U_NOEXCEPT;
+
+    // To give the fluent setters access to this class's constructor:
+    friend class NumberRangeFormatterSettings<UnlocalizedNumberRangeFormatter>;
+
+    // To give NumberRangeFormatter::with() access to this class's constructor:
+    friend class NumberRangeFormatter;
+};
+
+/**
+ * A NumberRangeFormatter that has a locale associated with it; this means .formatRange() methods are available.
+ *
+ * @see NumberFormatter
+ * @draft ICU 63
+ */
+class U_I18N_API LocalizedNumberRangeFormatter
+        : public NumberRangeFormatterSettings<LocalizedNumberRangeFormatter>, public UMemory {
+  public:
+    /**
+     * Format the given Formattables to a string using the settings specified in the NumberRangeFormatter fluent setting
+     * chain.
+     *
+     * @param first
+     *            The first number in the range, usually to the left in LTR locales.
+     * @param second
+     *            The second number in the range, usually to the right in LTR locales.
+     * @return A FormattedNumberRange object; call .toString() to get the string.
+     * @draft ICU 63
+     */
+    FormattedNumberRange formatFormattableRange(
+        const Formattable& first, const Formattable& second) const;
+
+    /**
+     * Default constructor: puts the formatter into a valid but undefined state.
+     *
+     * @draft ICU 63
+     */
+    LocalizedNumberRangeFormatter() = default;
+
+    // Make default copy constructor call the NumberRangeFormatterSettings copy constructor.
+    /**
+     * Returns a copy of this LocalizedNumberRangeFormatter.
+     * @draft ICU 63
+     */
+    LocalizedNumberRangeFormatter(const LocalizedNumberRangeFormatter &other);
+
+    /**
+     * Move constructor:
+     * The source LocalizedNumberRangeFormatter will be left in a valid but undefined state.
+     * @draft ICU 63
+     */
+    LocalizedNumberRangeFormatter(LocalizedNumberRangeFormatter&& src) U_NOEXCEPT;
+
+    /**
+     * Copy assignment operator.
+     * @draft ICU 63
+     */
+    LocalizedNumberRangeFormatter& operator=(const LocalizedNumberRangeFormatter& other);
+
+    /**
+     * Move assignment operator:
+     * The source LocalizedNumberRangeFormatter will be left in a valid but undefined state.
+     * @draft ICU 63
+     */
+    LocalizedNumberRangeFormatter& operator=(LocalizedNumberRangeFormatter&& src) U_NOEXCEPT;
+
+#ifndef U_HIDE_INTERNAL_API
+
+    /**
+     * @param results
+     *            The results object. This method will mutate it to save the results.
+     * @internal
+     */
+    void formatImpl(impl::UFormattedNumberRangeData* results, UErrorCode& status) const;
+
+#endif
+
+    /**
+     * Destruct this LocalizedNumberRangeFormatter, cleaning up any memory it might own.
+     * @draft ICU 63
+     */
+    ~LocalizedNumberRangeFormatter();
+
+  private:
+    // Note: fCompiled can't be a LocalPointer because impl::NumberFormatterImpl is defined in an internal
+    // header, and LocalPointer needs the full class definition in order to delete the instance.
+    const impl::NumberFormatterImpl* fCompiled {nullptr};
+    char fUnsafeCallCount[8] {};  // internally cast to u_atomic_int32_t
+
+    explicit LocalizedNumberRangeFormatter(
+        const NumberRangeFormatterSettings<LocalizedNumberRangeFormatter>& other);
+
+    explicit LocalizedNumberRangeFormatter(
+        NumberRangeFormatterSettings<LocalizedNumberRangeFormatter>&& src) U_NOEXCEPT;
+
+    LocalizedNumberRangeFormatter(const impl::RangeMacroProps &macros, const Locale &locale);
+
+    LocalizedNumberRangeFormatter(impl::RangeMacroProps &&macros, const Locale &locale);
+
+    // To give the fluent setters access to this class's constructor:
+    friend class NumberRangeFormatterSettings<UnlocalizedNumberRangeFormatter>;
+    friend class NumberRangeFormatterSettings<LocalizedNumberRangeFormatter>;
+
+    // To give UnlocalizedNumberRangeFormatter::locale() access to this class's constructor:
+    friend class UnlocalizedNumberRangeFormatter;
+};
+
+/**
+ * The result of a number range formatting operation. This class allows the result to be exported in several data types,
+ * including a UnicodeString and a FieldPositionIterator.
+ *
+ * @draft ICU 63
+ */
+class U_I18N_API FormattedNumberRange : public UMemory {
+  public:
+    /**
+     * Returns a UnicodeString representation of the formatted number range.
+     *
+     * @param status
+     *            Set if an error occurs while formatting the number to the UnicodeString.
+     * @return a UnicodeString containing the localized number range.
+     * @draft ICU 63
+     */
+    UnicodeString toString(UErrorCode& status) const;
+
+    /**
+     * Appends the formatted number range to an Appendable.
+     *
+     * @param appendable
+     *            The Appendable to which to append the formatted number range string.
+     * @param status
+     *            Set if an error occurs while formatting the number range to the Appendable.
+     * @return The same Appendable, for chaining.
+     * @draft ICU 63
+     * @see Appendable
+     */
+    Appendable &appendTo(Appendable &appendable, UErrorCode& status);
+
+    /**
+     * Determines the start and end indices of the next occurrence of the given <em>field</em> in the
+     * output string. This allows you to determine the locations of, for example, the integer part,
+     * fraction part, or symbols.
+     *
+     * If both sides of the range have the same field, the field will occur twice, once before the
+     * range separator and once after the range separator, if applicable.
+     *
+     * If a field occurs just once, calling this method will find that occurrence and return it. If a
+     * field occurs multiple times, this method may be called repeatedly with the following pattern:
+     *
+     * <pre>
+     * FieldPosition fpos(UNUM_INTEGER_FIELD);
+     * while (formattedNumberRange.nextFieldPosition(fpos, status)) {
+     *   // do something with fpos.
+     * }
+     * </pre>
+     *
+     * This method is useful if you know which field to query. If you want all available field position
+     * information, use #getAllFieldPositions().
+     *
+     * @param fieldPosition
+     *            Input+output variable. See {@link FormattedNumber#nextFieldPosition(FieldPosition)}.
+     * @param status
+     *            Set if an error occurs while populating the FieldPosition.
+     * @return TRUE if a new occurrence of the field was found; FALSE otherwise.
+     * @draft ICU 63
+     * @see UNumberFormatFields
+     */
+    UBool nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const;
+
+    /**
+     * Export the formatted number range to a FieldPositionIterator. This allows you to determine which characters in
+     * the output string correspond to which <em>fields</em>, such as the integer part, fraction part, and sign.
+     *
+     * If information on only one field is needed, use #nextFieldPosition() instead.
+     *
+     * @param iterator
+     *            The FieldPositionIterator to populate with all of the fields present in the formatted number.
+     * @param status
+     *            Set if an error occurs while populating the FieldPositionIterator.
+     * @draft ICU 63
+     * @see UNumberFormatFields
+     */
+    void getAllFieldPositions(FieldPositionIterator &iterator, UErrorCode &status) const;
+
+    /**
+     * Export the first formatted number as a decimal number. This endpoint
+     * is useful for obtaining the exact number being printed after scaling
+     * and rounding have been applied by the number range formatting pipeline.
+     * 
+     * The syntax of the unformatted number is a "numeric string"
+     * as defined in the Decimal Arithmetic Specification, available at
+     * http://speleotrove.com/decimal
+     *
+     * @return A decimal representation of the first formatted number.
+     * @draft ICU 63
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberRangeFormatter
+     * @see #getSecondDecimal
+     */
+    UnicodeString getFirstDecimal() const;
+
+    /**
+     * Export the second formatted number as a decimal number. This endpoint
+     * is useful for obtaining the exact number being printed after scaling
+     * and rounding have been applied by the number range formatting pipeline.
+     * 
+     * The syntax of the unformatted number is a "numeric string"
+     * as defined in the Decimal Arithmetic Specification, available at
+     * http://speleotrove.com/decimal
+     *
+     * @return A decimal representation of the second formatted number.
+     * @draft ICU 63
+     * @provisional This API might change or be removed in a future release.
+     * @see NumberRangeFormatter
+     * @see #getFirstDecimal
+     */
+    UnicodeString getSecondDecimal() const;
+
+    /**
+     * Returns whether the pair of numbers was successfully formatted as a range or whether an identity fallback was
+     * used. For example, if the first and second number were the same either before or after rounding occurred, an
+     * identity fallback was used.
+     *
+     * @return An indication the resulting identity situation in the formatted number range.
+     * @draft ICU 63
+     * @provisional This API might change or be removed in a future release.
+     * @see UNumberRangeIdentityFallback
+     */
+    UNumberRangeIdentityType getIdentityType() const;
+
+    /**
+     * Copying not supported; use move constructor instead.
+     */
+    FormattedNumberRange(const FormattedNumberRange&) = delete;
+
+    /**
+     * Copying not supported; use move assignment instead.
+     */
+    FormattedNumberRange& operator=(const FormattedNumberRange&) = delete;
+
+    /**
+     * Move constructor:
+     * Leaves the source FormattedNumberRange in an undefined state.
+     * @draft ICU 63
+     */
+    FormattedNumberRange(FormattedNumberRange&& src) U_NOEXCEPT;
+
+    /**
+     * Move assignment:
+     * Leaves the source FormattedNumberRange in an undefined state.
+     * @draft ICU 63
+     */
+    FormattedNumberRange& operator=(FormattedNumberRange&& src) U_NOEXCEPT;
+
+    /**
+     * Destruct an instance of FormattedNumberRange, cleaning up any memory it might own.
+     * @draft ICU 63
+     */
+    ~FormattedNumberRange();
+
+  private:
+    // Can't use LocalPointer because UFormattedNumberRangeData is forward-declared
+    const impl::UFormattedNumberRangeData *fResults;
+
+    // Error code for the terminal methods
+    UErrorCode fErrorCode;
+
+    /**
+     * Internal constructor from data type. Adopts the data pointer.
+     * @internal
+     */
+    explicit FormattedNumberRange(impl::UFormattedNumberRangeData *results)
+        : fResults(results), fErrorCode(U_ZERO_ERROR) {};
+
+    explicit FormattedNumberRange(UErrorCode errorCode)
+        : fResults(nullptr), fErrorCode(errorCode) {};
+
+    // To give LocalizedNumberRangeFormatter format methods access to this class's constructor:
+    friend class LocalizedNumberRangeFormatter;
+};
+
+/**
+ * See the main description in numberrangeformatter.h for documentation and examples.
+ *
+ * @draft ICU 63
+ */
+class U_I18N_API NumberRangeFormatter final {
+  public:
+    /**
+     * Call this method at the beginning of a NumberRangeFormatter fluent chain in which the locale is not currently
+     * known at the call site.
+     *
+     * @return An {@link UnlocalizedNumberRangeFormatter}, to be used for chaining.
+     * @draft ICU 63
+     */
+    static UnlocalizedNumberFormatter with();
+
+    /**
+     * Call this method at the beginning of a NumberRangeFormatter fluent chain in which the locale is known at the call
+     * site.
+     *
+     * @param locale
+     *            The locale from which to load formats and symbols for number range formatting.
+     * @return A {@link LocalizedNumberRangeFormatter}, to be used for chaining.
+     * @draft ICU 63
+     */
+    static LocalizedNumberFormatter withLocale(const Locale &locale);
+
+    /**
+     * Use factory methods instead of the constructor to create a NumberFormatter.
+     */
+    NumberRangeFormatter() = delete;
+};
+
+}  // namespace number
+U_NAMESPACE_END
+
+#endif  // U_HIDE_DRAFT_API
+
+#endif // __NUMBERRANGEFORMATTER_H__
+
+#endif /* #if !UCONFIG_NO_FORMATTING */