UFormattedNumberRangeApiHelper;
+
+struct UFormattedNumberRangeImpl : public UFormattedValueImpl, public UFormattedNumberRangeApiHelper {
+ UFormattedNumberRangeImpl();
+ ~UFormattedNumberRangeImpl();
+
+ FormattedNumberRange fImpl;
+ UFormattedNumberRangeData fData;
+};
+
+UFormattedNumberRangeImpl::UFormattedNumberRangeImpl()
+ : fImpl(&fData) {
+ fFormattedValue = &fImpl;
+}
+
+UFormattedNumberRangeImpl::~UFormattedNumberRangeImpl() {
+ // Disown the data from fImpl so it doesn't get deleted twice
+ fImpl.fData = nullptr;
+}
+
+}
+}
+U_NAMESPACE_END
+
+
+UPRV_FORMATTED_VALUE_CAPI_NO_IMPLTYPE_AUTO_IMPL(
+ UFormattedNumberRange,
+ UFormattedNumberRangeImpl,
+ UFormattedNumberRangeApiHelper,
+ unumrf)
+
+
+U_CAPI UNumberRangeFormatter* U_EXPORT2
+unumrf_openForSkeletonWithCollapseAndIdentityFallback(
+ const UChar* skeleton,
+ int32_t skeletonLen,
+ UNumberRangeCollapse collapse,
+ UNumberRangeIdentityFallback identityFallback,
+ const char* locale,
+ UParseError* perror,
+ UErrorCode* ec) {
+ auto* impl = new UNumberRangeFormatterData();
+ if (impl == nullptr) {
+ *ec = U_MEMORY_ALLOCATION_ERROR;
+ return nullptr;
+ }
+ // Readonly-alias constructor (first argument is whether we are NUL-terminated)
+ UnicodeString skeletonString(skeletonLen == -1, skeleton, skeletonLen);
+ impl->fFormatter = NumberRangeFormatter::withLocale(locale)
+ .numberFormatterBoth(NumberFormatter::forSkeleton(skeletonString, *perror, *ec))
+ .collapse(collapse)
+ .identityFallback(identityFallback);
+ return impl->exportForC();
+}
+
+U_CAPI void U_EXPORT2
+unumrf_formatDoubleRange(
+ const UNumberRangeFormatter* uformatter,
+ double first,
+ double second,
+ UFormattedNumberRange* uresult,
+ UErrorCode* ec) {
+ const UNumberRangeFormatterData* formatter = UNumberRangeFormatterData::validate(uformatter, *ec);
+ auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec);
+ if (U_FAILURE(*ec)) { return; }
+
+ result->fData.getStringRef().clear();
+ result->fData.quantity1.setToDouble(first);
+ result->fData.quantity2.setToDouble(second);
+ formatter->fFormatter.formatImpl(result->fData, first == second, *ec);
+}
+
+U_CAPI void U_EXPORT2
+unumrf_formatDecimalRange(
+ const UNumberRangeFormatter* uformatter,
+ const char* first, int32_t firstLen,
+ const char* second, int32_t secondLen,
+ UFormattedNumberRange* uresult,
+ UErrorCode* ec) {
+ const UNumberRangeFormatterData* formatter = UNumberRangeFormatterData::validate(uformatter, *ec);
+ auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec);
+ if (U_FAILURE(*ec)) { return; }
+
+ result->fData.getStringRef().clear();
+ result->fData.quantity1.setToDecNumber({first, firstLen}, *ec);
+ result->fData.quantity2.setToDecNumber({second, secondLen}, *ec);
+ formatter->fFormatter.formatImpl(result->fData, first == second, *ec);
+}
+
+U_CAPI UNumberRangeIdentityResult U_EXPORT2
+unumrf_resultGetIdentityResult(
+ const UFormattedNumberRange* uresult,
+ UErrorCode* ec) {
+ auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec);
+ if (U_FAILURE(*ec)) {
+ return UNUM_IDENTITY_RESULT_COUNT;
+ }
+ return result->fData.identityResult;
+}
+
+U_CAPI void U_EXPORT2
+unumrf_close(UNumberRangeFormatter* f) {
+ UErrorCode localStatus = U_ZERO_ERROR;
+ const UNumberRangeFormatterData* impl = UNumberRangeFormatterData::validate(f, localStatus);
+ delete impl;
+}
+
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/sources.txt b/icu4c/source/i18n/sources.txt
index c4b84de32df..335e5ee892a 100644
--- a/icu4c/source/i18n/sources.txt
+++ b/icu4c/source/i18n/sources.txt
@@ -133,6 +133,7 @@ numparse_parsednumber.cpp
numparse_scientific.cpp
numparse_symbols.cpp
numparse_validators.cpp
+numrange_capi.cpp
numrange_fluent.cpp
numrange_impl.cpp
numsys.cpp
diff --git a/icu4c/source/i18n/unicode/numberrangeformatter.h b/icu4c/source/i18n/unicode/numberrangeformatter.h
index 4d436a76945..bfbf27468f0 100644
--- a/icu4c/source/i18n/unicode/numberrangeformatter.h
+++ b/icu4c/source/i18n/unicode/numberrangeformatter.h
@@ -16,6 +16,7 @@
#include "unicode/formattedvalue.h"
#include "unicode/fpositer.h"
#include "unicode/numberformatter.h"
+#include "unicode/unumberrangeformatter.h"
/**
* \file
@@ -31,7 +32,7 @@
* .numberFormatterFirst(NumberFormatter::with().adoptUnit(MeasureUnit::createMeter()))
* .numberFormatterSecond(NumberFormatter::with().adoptUnit(MeasureUnit::createKilometer()))
* .locale("en-GB")
- * .formatRange(750, 1.2, status)
+ * .formatFormattableRange(750, 1.2, status)
* .toString(status);
* // => "750 m - 1.2 km"
*
@@ -44,128 +45,6 @@
*/
-/**
- * Defines how to merge fields that are identical across the range sign.
- *
- * @stable 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.
- *
- * The heuristics used for this option are subject to change over time.
- *
- * @stable ICU 63
- */
- UNUM_RANGE_COLLAPSE_AUTO,
-
- /**
- * Do not collapse any part of the number. Example: "3.2 thousand kilograms – 5.3 thousand kilograms"
- *
- * @stable 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"
- *
- * @stable 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"
- *
- * @stable 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.
- *
- * @stable ICU 63
- * @see NumberRangeFormatter
- */
-typedef enum UNumberRangeIdentityFallback {
- /**
- * Show the number as a single value rather than a range. Example: "$5"
- *
- * @stable 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"
- *
- * @stable 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"
- *
- * @stable 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"
- *
- * @stable ICU 63
- */
- UNUM_IDENTITY_FALLBACK_RANGE
-} UNumberRangeIdentityFallback;
-
-/**
- * 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.
- *
- * @stable ICU 63
- * @see NumberRangeFormatter
- */
-typedef enum UNumberRangeIdentityResult {
- /**
- * Used to indicate that the two numbers in the range were equal, even before any rounding rules were applied.
- *
- * @stable ICU 63
- * @see NumberRangeFormatter
- */
- UNUM_IDENTITY_RESULT_EQUAL_BEFORE_ROUNDING,
-
- /**
- * Used to indicate that the two numbers in the range were equal, but only after rounding rules were applied.
- *
- * @stable ICU 63
- * @see NumberRangeFormatter
- */
- UNUM_IDENTITY_RESULT_EQUAL_AFTER_ROUNDING,
-
- /**
- * Used to indicate that the two numbers in the range were not equal, even after rounding rules were applied.
- *
- * @stable ICU 63
- * @see NumberRangeFormatter
- */
- UNUM_IDENTITY_RESULT_NOT_EQUAL,
-
-#ifndef U_HIDE_INTERNAL_API
- /**
- * The number of entries in this enum.
- * @internal
- */
- UNUM_IDENTITY_RESULT_COUNT
-#endif
-
-} UNumberRangeIdentityResult;
-
U_NAMESPACE_BEGIN
namespace number { // icu::number
@@ -182,6 +61,7 @@ struct RangeMacroProps;
class DecimalQuantity;
class UFormattedNumberRangeData;
class NumberRangeFormatterImpl;
+struct UFormattedNumberRangeImpl;
} // namespace impl
@@ -418,8 +298,8 @@ class U_I18N_API NumberRangeFormatterSettings {
/**
* 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:
+ * passed to the formatFormattableRange function, or if different numbers are passed to the function but they
+ * become the same after rounding rules are applied. Possible values:
*
*
* - SINGLE_VALUE: "5 miles"
@@ -820,6 +700,9 @@ class U_I18N_API FormattedNumberRange : public UMemory, public FormattedValue {
// To give LocalizedNumberRangeFormatter format methods access to this class's constructor:
friend class LocalizedNumberRangeFormatter;
+
+ // To give C API access to internals
+ friend struct impl::UFormattedNumberRangeImpl;
};
/**
diff --git a/icu4c/source/i18n/unicode/unumberformatter.h b/icu4c/source/i18n/unicode/unumberformatter.h
index d91f1486f8f..83bb9b625f2 100644
--- a/icu4c/source/i18n/unicode/unumberformatter.h
+++ b/icu4c/source/i18n/unicode/unumberformatter.h
@@ -1,11 +1,12 @@
// © 2018 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
+#ifndef __UNUMBERFORMATTER_H__
+#define __UNUMBERFORMATTER_H__
+
#include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING
-#ifndef __UNUMBERFORMATTER_H__
-#define __UNUMBERFORMATTER_H__
#include "unicode/parseerr.h"
#include "unicode/ufieldpositer.h"
@@ -725,5 +726,5 @@ U_DEFINE_LOCAL_OPEN_POINTER(LocalUFormattedNumberPointer, UFormattedNumber, unum
U_NAMESPACE_END
#endif // U_SHOW_CPLUSPLUS_API
-#endif //__UNUMBERFORMATTER_H__
#endif /* #if !UCONFIG_NO_FORMATTING */
+#endif //__UNUMBERFORMATTER_H__
diff --git a/icu4c/source/i18n/unicode/unumberrangeformatter.h b/icu4c/source/i18n/unicode/unumberrangeformatter.h
new file mode 100644
index 00000000000..6ed9cba5be9
--- /dev/null
+++ b/icu4c/source/i18n/unicode/unumberrangeformatter.h
@@ -0,0 +1,432 @@
+// © 2020 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#ifndef __UNUMBERRANGEFORMATTER_H__
+#define __UNUMBERRANGEFORMATTER_H__
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/parseerr.h"
+#include "unicode/ufieldpositer.h"
+#include "unicode/umisc.h"
+#include "unicode/uformattedvalue.h"
+#include "unicode/uformattable.h"
+
+
+/**
+ * \file
+ * \brief C-compatible API for localized number range formatting.
+ *
+ * This is the C-compatible version of the NumberRangeFormatter API. C++ users
+ * should include unicode/numberrangeformatter.h and use the proper C++ APIs.
+ *
+ * First create a UNumberRangeFormatter, which is immutable, and then format to
+ * a UFormattedNumberRange.
+ *
+ * Example code:
+ *
+ * // Setup:
+ * UErrorCode ec = U_ZERO_ERROR;
+ * UNumberRangeFormatter* uformatter = unumrf_openForSkeletonCollapseIdentityFallbackAndLocaleWithError(
+ * u"currency/USD precision-integer",
+ * -1,
+ * UNUM_RANGE_COLLAPSE_AUTO,
+ * UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
+ * "en-US",
+ * NULL,
+ * &ec);
+ * UFormattedNumberRange* uresult = unumrf_openResult(&ec);
+ * if (U_FAILURE(ec)) { return; }
+ *
+ * // Format a double range:
+ * unumrf_formatDoubleRange(uformatter, 3.0, 5.0, uresult, &ec);
+ * if (U_FAILURE(ec)) { return; }
+ *
+ * // Get the result string:
+ * int32_t len;
+ * const UChar* str = ufmtval_getString(unumrf_resultAsValue(uresult, &ec), &len, &ec);
+ * if (U_FAILURE(ec)) { return; }
+ * // str should equal "$3 – $5"
+ *
+ * // Cleanup:
+ * unumf_close(uformatter);
+ * unumf_closeResult(uresult);
+ *
+ *
+ * If you are a C++ user linking against the C libraries, you can use the LocalPointer versions of these
+ * APIs. The following example uses LocalPointer with the decimal number and field position APIs:
+ *
+ *
+ * // Setup:
+ * LocalUNumberRangeFormatterPointer uformatter(
+ * unumrf_openForSkeletonCollapseIdentityFallbackAndLocaleWithError(...));
+ * LocalUFormattedNumberRangePointer uresult(unumrf_openResult(&ec));
+ * if (U_FAILURE(ec)) { return; }
+ *
+ * // Format a double number range:
+ * unumrf_formatDoubleRange(uformatter.getAlias(), 3.0, 5.0, uresult.getAlias(), &ec);
+ * if (U_FAILURE(ec)) { return; }
+ *
+ * // No need to do any cleanup since we are using LocalPointer.
+ *
+ *
+ * You can also get field positions. For more information, see uformattedvalue.h.
+ */
+
+/**
+ * Defines how to merge fields that are identical across the range sign.
+ *
+ * @stable 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.
+ *
+ * The heuristics used for this option are subject to change over time.
+ *
+ * @stable ICU 63
+ */
+ UNUM_RANGE_COLLAPSE_AUTO,
+
+ /**
+ * Do not collapse any part of the number. Example: "3.2 thousand kilograms – 5.3 thousand kilograms"
+ *
+ * @stable 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"
+ *
+ * @stable 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"
+ *
+ * @stable 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.
+ *
+ * @stable ICU 63
+ * @see NumberRangeFormatter
+ */
+typedef enum UNumberRangeIdentityFallback {
+ /**
+ * Show the number as a single value rather than a range. Example: "$5"
+ *
+ * @stable 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"
+ *
+ * @stable 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"
+ *
+ * @stable 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"
+ *
+ * @stable ICU 63
+ */
+ UNUM_IDENTITY_FALLBACK_RANGE
+} UNumberRangeIdentityFallback;
+
+/**
+ * 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.
+ *
+ * @stable ICU 63
+ * @see NumberRangeFormatter
+ */
+typedef enum UNumberRangeIdentityResult {
+ /**
+ * Used to indicate that the two numbers in the range were equal, even before any rounding rules were applied.
+ *
+ * @stable ICU 63
+ * @see NumberRangeFormatter
+ */
+ UNUM_IDENTITY_RESULT_EQUAL_BEFORE_ROUNDING,
+
+ /**
+ * Used to indicate that the two numbers in the range were equal, but only after rounding rules were applied.
+ *
+ * @stable ICU 63
+ * @see NumberRangeFormatter
+ */
+ UNUM_IDENTITY_RESULT_EQUAL_AFTER_ROUNDING,
+
+ /**
+ * Used to indicate that the two numbers in the range were not equal, even after rounding rules were applied.
+ *
+ * @stable ICU 63
+ * @see NumberRangeFormatter
+ */
+ UNUM_IDENTITY_RESULT_NOT_EQUAL,
+
+#ifndef U_HIDE_INTERNAL_API
+ /**
+ * The number of entries in this enum.
+ * @internal
+ */
+ UNUM_IDENTITY_RESULT_COUNT
+#endif
+
+} UNumberRangeIdentityResult;
+
+
+#ifndef U_HIDE_DRAFT_API
+
+struct UNumberRangeFormatter;
+/**
+ * C-compatible version of icu::number::LocalizedNumberRangeFormatter.
+ *
+ * NOTE: This is a C-compatible API; C++ users should build against numberrangeformatter.h instead.
+ *
+ * @draft ICU 68
+ */
+typedef struct UNumberRangeFormatter UNumberRangeFormatter;
+
+
+struct UFormattedNumberRange;
+/**
+ * C-compatible version of icu::number::FormattedNumberRange.
+ *
+ * NOTE: This is a C-compatible API; C++ users should build against numberrangeformatter.h instead.
+ *
+ * @draft ICU 68
+ */
+typedef struct UFormattedNumberRange UFormattedNumberRange;
+
+
+/**
+ * Creates a new UNumberFormatter for the given skeleton string, collapse option, identity fallback
+ * option, and locale. This is currently the only method for creating a new UNumberRangeFormatter.
+ *
+ * Objects of type UNumberRangeFormatter returned by this method are threadsafe.
+ *
+ * For more details on skeleton strings, see the documentation in numberrangeformatter.h. For more
+ * details on the usage of this API, see the documentation at the top of unumberrangeformatter.h.
+ *
+ * NOTE: This is a C-compatible API; C++ users should build against numberrangeformatter.h instead.
+ *
+ * @param skeleton The skeleton string, like u"percent precision-integer"
+ * @param skeletonLen The number of UChars in the skeleton string, or -1 if it is NUL-terminated.
+ * @param collapse Option for how to merge affixes (if unsure, use UNUM_RANGE_COLLAPSE_AUTO)
+ * @param identityFallback Option for resolving when both sides of the range are equal.
+ * @param locale The NUL-terminated locale ID.
+ * @param perror A parse error struct populated if an error occurs when parsing. Can be NULL.
+ * If no error occurs, perror->offset will be set to -1.
+ * @param ec Set if an error occurs.
+ * @draft ICU 68
+ */
+U_STABLE UNumberRangeFormatter* U_EXPORT2
+unumrf_openForSkeletonWithCollapseAndIdentityFallback(
+ const UChar* skeleton,
+ int32_t skeletonLen,
+ UNumberRangeCollapse collapse,
+ UNumberRangeIdentityFallback identityFallback,
+ const char* locale,
+ UParseError* perror,
+ UErrorCode* ec);
+
+
+/**
+ * Creates an object to hold the result of a UNumberRangeFormatter
+ * operation. The object can be used repeatedly; it is cleared whenever
+ * passed to a format function.
+ *
+ * @param ec Set if an error occurs.
+ * @draft ICU 68
+ */
+U_STABLE UFormattedNumberRange* U_EXPORT2
+unumrf_openResult(UErrorCode* ec);
+
+
+/**
+ * Uses a UNumberRangeFormatter to format a range of doubles.
+ *
+ * The UNumberRangeFormatter can be shared between threads. Each thread should have its own local
+ * UFormattedNumberRange, however, for storing the result of the formatting operation.
+ *
+ * NOTE: This is a C-compatible API; C++ users should build against numberrangeformatter.h instead.
+ *
+ * @param uformatter A formatter object; see unumberrangeformatter.h.
+ * @param first The first (usually smaller) number in the range.
+ * @param second The second (usually larger) number in the range.
+ * @param uresult The object that will be mutated to store the result; see unumrf_openResult.
+ * @param ec Set if an error occurs.
+ * @draft ICU 68
+ */
+U_STABLE void U_EXPORT2
+unumrf_formatDoubleRange(
+ const UNumberRangeFormatter* uformatter,
+ double first,
+ double second,
+ UFormattedNumberRange* uresult,
+ UErrorCode* ec);
+
+
+/**
+ * Uses a UNumberRangeFormatter to format a range of decimal numbers.
+ *
+ * With a decimal number string, you can specify an input with arbitrary precision.
+ *
+ * The UNumberRangeFormatter can be shared between threads. Each thread should have its own local
+ * UFormattedNumberRange, however, for storing the result of the formatting operation.
+ *
+ * NOTE: This is a C-compatible API; C++ users should build against numberrangeformatter.h instead.
+ *
+ * @param uformatter A formatter object; see unumberrangeformatter.h.
+ * @param first The first (usually smaller) number in the range.
+ * @param firstLen The length of the first decimal number string.
+ * @param second The second (usually larger) number in the range.
+ * @param secondLen The length of the second decimal number string.
+ * @param uresult The object that will be mutated to store the result; see unumrf_openResult.
+ * @param ec Set if an error occurs.
+ * @draft ICU 68
+ */
+U_STABLE void U_EXPORT2
+unumrf_formatDecimalRange(
+ const UNumberRangeFormatter* uformatter,
+ const char* first,
+ int32_t firstLen,
+ const char* second,
+ int32_t secondLen,
+ UFormattedNumberRange* uresult,
+ UErrorCode* ec);
+
+
+/**
+ * Returns a representation of a UFormattedNumberRange as a UFormattedValue,
+ * which can be subsequently passed to any API requiring that type.
+ *
+ * The returned object is owned by the UFormattedNumberRange and is valid
+ * only as long as the UFormattedNumber is present and unchanged in memory.
+ *
+ * You can think of this method as a cast between types.
+ *
+ * @param uresult The object containing the formatted number range.
+ * @param ec Set if an error occurs.
+ * @return A UFormattedValue owned by the input object.
+ * @draft ICU 68
+ */
+U_STABLE const UFormattedValue* U_EXPORT2
+unumrf_resultAsValue(const UFormattedNumberRange* uresult, UErrorCode* ec);
+
+
+/**
+ * Extracts the identity result from a UFormattedNumberRange.
+ *
+ * NOTE: This is a C-compatible API; C++ users should build against numberformatter.h instead.
+ *
+ * @param uresult The object containing the formatted number range.
+ * @param ec Set if an error occurs.
+ * @return The identity result; see UNumberRangeIdentityResult.
+ * @draft ICU 68
+ */
+U_STABLE UNumberRangeIdentityResult U_EXPORT2
+unumrf_resultGetIdentityResult(
+ const UFormattedNumberRange* uresult,
+ UErrorCode* ec);
+
+
+
+// TODO(ICU-20775): Propose these as API.
+// NOTE: This is not currently implemented.
+// U_DRAFT int32_t U_EXPORT2
+// unumf_resultGetFirstDecimal(const UFormattedNumberRange* uresult, char* buffer, int32_t bufferCapacity,
+// UErrorCode* ec);
+// U_DRAFT int32_t U_EXPORT2
+// unumf_resultGetSecondDecimal(const UFormattedNumberRange* uresult, char* buffer, int32_t bufferCapacity,
+// UErrorCode* ec);
+
+
+/**
+ * Releases the UNumberFormatter created by unumf_openForSkeletonAndLocale().
+ *
+ * @param uformatter An object created by unumf_openForSkeletonAndLocale().
+ * @draft ICU 68
+ */
+U_STABLE void U_EXPORT2
+unumrf_close(UNumberRangeFormatter* uformatter);
+
+
+/**
+ * Releases the UFormattedNumber created by unumf_openResult().
+ *
+ * @param uresult An object created by unumf_openResult().
+ * @draft ICU 68
+ */
+U_STABLE void U_EXPORT2
+unumrf_closeResult(UFormattedNumberRange* uresult);
+
+
+#if U_SHOW_CPLUSPLUS_API
+U_NAMESPACE_BEGIN
+
+/**
+ * \class LocalUNumberRangeFormatterPointer
+ * "Smart pointer" class; closes a UNumberFormatter via unumf_close().
+ * For most methods see the LocalPointerBase base class.
+ *
+ * Usage:
+ *
+ * LocalUNumberRangeFormatterPointer uformatter(
+ * unumrf_openForSkeletonCollapseIdentityFallbackAndLocaleWithError(...));
+ * // no need to explicitly call unumrf_close()
+ *
+ *
+ * @see LocalPointerBase
+ * @see LocalPointer
+ * @draft ICU 68
+ */
+U_DEFINE_LOCAL_OPEN_POINTER(LocalUNumberRangeFormatterPointer, UNumberRangeFormatter, unumrf_close);
+
+/**
+ * \class LocalUFormattedNumberPointer
+ * "Smart pointer" class; closes a UFormattedNumber via unumf_closeResult().
+ * For most methods see the LocalPointerBase base class.
+ *
+ * Usage:
+ *
+ * LocalUFormattedNumberRangePointer uresult(unumrf_openResult(...));
+ * // no need to explicitly call unumrf_closeResult()
+ *
+ *
+ * @see LocalPointerBase
+ * @see LocalPointer
+ * @draft ICU 68
+ */
+U_DEFINE_LOCAL_OPEN_POINTER(LocalUFormattedNumberRangePointer, UFormattedNumberRange, unumrf_closeResult);
+
+U_NAMESPACE_END
+#endif // U_SHOW_CPLUSPLUS_API
+
+#endif // U_HIDE_DRAFT_API
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+#endif //__UNUMBERRANGEFORMATTER_H__
diff --git a/icu4c/source/test/cintltst/Makefile.in b/icu4c/source/test/cintltst/Makefile.in
index a8038378409..34f8ea2acdd 100644
--- a/icu4c/source/test/cintltst/Makefile.in
+++ b/icu4c/source/test/cintltst/Makefile.in
@@ -55,7 +55,7 @@ hpmufn.o tracetst.o reapits.o uregiontest.o ulistfmttest.o\
utexttst.o ucsdetst.o spooftest.o \
cbiditransformtst.o \
cgendtst.o \
-unumberformattertst.o uformattedvaluetst.o
+unumberformattertst.o uformattedvaluetst.o unumberrangeformattertst.o
DEPS = $(OBJECTS:.o=.d)
diff --git a/icu4c/source/test/cintltst/cformtst.c b/icu4c/source/test/cintltst/cformtst.c
index f4e62b73906..f9c3ac38892 100644
--- a/icu4c/source/test/cintltst/cformtst.c
+++ b/icu4c/source/test/cintltst/cformtst.c
@@ -41,6 +41,7 @@ void addURegionTest(TestNode** root);
void addUListFmtTest(TestNode** root);
void addUNumberFormatterTest(TestNode** root);
void addUFormattedValueTest(TestNode** root);
+void addUNumberRangeFormatterTest(TestNode** root);
void addFormatTest(TestNode** root);
@@ -65,6 +66,7 @@ void addFormatTest(TestNode** root)
addUListFmtTest(root);
addUNumberFormatterTest(root);
addUFormattedValueTest(root);
+ addUNumberRangeFormatterTest(root);
}
/*Internal functions used*/
diff --git a/icu4c/source/test/cintltst/cintltst.vcxproj b/icu4c/source/test/cintltst/cintltst.vcxproj
index 9444d3a634d..2f548636e26 100644
--- a/icu4c/source/test/cintltst/cintltst.vcxproj
+++ b/icu4c/source/test/cintltst/cintltst.vcxproj
@@ -175,6 +175,7 @@
+
diff --git a/icu4c/source/test/cintltst/cintltst.vcxproj.filters b/icu4c/source/test/cintltst/cintltst.vcxproj.filters
index b541163663e..d39436d4451 100644
--- a/icu4c/source/test/cintltst/cintltst.vcxproj.filters
+++ b/icu4c/source/test/cintltst/cintltst.vcxproj.filters
@@ -225,6 +225,9 @@
formatting
+
+ formatting
+
locales & resources
diff --git a/icu4c/source/test/cintltst/unumberrangeformattertst.c b/icu4c/source/test/cintltst/unumberrangeformattertst.c
new file mode 100644
index 00000000000..8dcc5c1ec58
--- /dev/null
+++ b/icu4c/source/test/cintltst/unumberrangeformattertst.c
@@ -0,0 +1,152 @@
+// © 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
+
+// Allow implicit conversion from char16_t* to UnicodeString for this file:
+// Helpful in toString methods and elsewhere.
+#define UNISTR_FROM_STRING_EXPLICIT
+
+#include
+#include "unicode/unumberformatter.h"
+#include "unicode/unumberrangeformatter.h"
+#include "unicode/umisc.h"
+#include "unicode/unum.h"
+#include "unicode/ustring.h"
+#include "cformtst.h"
+#include "cintltst.h"
+#include "cmemory.h"
+
+static void TestExampleCode(void);
+
+static void TestFormattedValue(void);
+
+static void TestSkeletonParseError(void);
+
+void addUNumberRangeFormatterTest(TestNode** root);
+
+#define TESTCASE(x) addTest(root, &x, "tsformat/unumberrangeformatter/" #x)
+
+void addUNumberRangeFormatterTest(TestNode** root) {
+ TESTCASE(TestExampleCode);
+ TESTCASE(TestFormattedValue);
+ TESTCASE(TestSkeletonParseError);
+}
+
+
+static void TestExampleCode() {
+ // This is the example code given in unumberrangeformatter.h.
+
+ // Setup:
+ UErrorCode ec = U_ZERO_ERROR;
+ UNumberRangeFormatter* uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
+ u"currency/USD precision-integer",
+ -1,
+ UNUM_RANGE_COLLAPSE_AUTO,
+ UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
+ "en-US",
+ NULL,
+ &ec);
+ UFormattedNumberRange* uresult = unumrf_openResult(&ec);
+ assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE);
+
+ // Format a double range:
+ unumrf_formatDoubleRange(uformatter, 3.0, 5.0, uresult, &ec);
+ assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE);
+
+ // Get the result string:
+ int32_t len;
+ const UChar* str = ufmtval_getString(unumrf_resultAsValue(uresult, &ec), &len, &ec);
+ assertSuccessCheck("There should not be a failure in the example code", &ec, TRUE);
+ assertUEquals("Should produce expected string result", u"$3 – $5", str);
+
+ // Cleanup:
+ unumrf_close(uformatter);
+ unumrf_closeResult(uresult);
+}
+
+
+static void TestFormattedValue() {
+ UErrorCode ec = U_ZERO_ERROR;
+ UNumberRangeFormatter* uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
+ u"K",
+ -1,
+ UNUM_RANGE_COLLAPSE_AUTO,
+ UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
+ "en-US",
+ NULL,
+ &ec);
+ assertSuccessCheck("Should create without error", &ec, TRUE);
+ UFormattedNumberRange* uresult = unumrf_openResult(&ec);
+ assertSuccess("Should create result without error", &ec);
+
+ // Test the decimal number code path, too
+ unumrf_formatDecimalRange(uformatter, "5.5e4", -1, "1.5e5", -1, uresult, &ec);
+
+ if (assertSuccessCheck("Should format without error", &ec, TRUE)) {
+ const UFormattedValue* fv = unumrf_resultAsValue(uresult, &ec);
+ assertSuccess("Should convert without error", &ec);
+ static const UFieldPosition expectedFieldPositions[] = {
+ // field, begin index, end index
+ {UNUM_INTEGER_FIELD, 0, 2},
+ {UNUM_COMPACT_FIELD, 2, 3},
+ {UNUM_INTEGER_FIELD, 6, 9},
+ {UNUM_COMPACT_FIELD, 9, 10}};
+ checkFormattedValue(
+ "FormattedNumber as FormattedValue",
+ fv,
+ u"55K – 150K",
+ UFIELD_CATEGORY_NUMBER,
+ expectedFieldPositions,
+ UPRV_LENGTHOF(expectedFieldPositions));
+ }
+
+ assertIntEquals("Identity result should match",
+ UNUM_IDENTITY_RESULT_NOT_EQUAL,
+ unumrf_resultGetIdentityResult(uresult, &ec));
+
+ // cleanup:
+ unumrf_closeResult(uresult);
+ unumrf_close(uformatter);
+}
+
+
+static void TestSkeletonParseError() {
+ UErrorCode ec = U_ZERO_ERROR;
+ UNumberRangeFormatter* uformatter;
+ UParseError perror;
+
+ // The UParseError can be null. The following should not segfault.
+ uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
+ u".00 measure-unit/typo",
+ -1,
+ UNUM_RANGE_COLLAPSE_AUTO,
+ UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
+ "en",
+ NULL,
+ &ec);
+ unumrf_close(uformatter);
+
+ // Now test the behavior.
+ ec = U_ZERO_ERROR;
+ uformatter = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
+ u".00 measure-unit/typo",
+ -1,
+ UNUM_RANGE_COLLAPSE_AUTO,
+ UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
+ "en",
+ &perror,
+ &ec);
+
+ assertIntEquals("Should have set error code", U_NUMBER_SKELETON_SYNTAX_ERROR, ec);
+ assertIntEquals("Should have correct skeleton error offset", 17, perror.offset);
+ assertUEquals("Should have correct pre context", u"0 measure-unit/", perror.preContext);
+ assertUEquals("Should have correct post context", u"typo", perror.postContext);
+
+ // cleanup:
+ unumrf_close(uformatter);
+}
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/test/depstest/dependencies.txt b/icu4c/source/test/depstest/dependencies.txt
index abaa74b7030..9dfad52daec 100644
--- a/icu4c/source/test/depstest/dependencies.txt
+++ b/icu4c/source/test/depstest/dependencies.txt
@@ -1000,7 +1000,7 @@ group: numberformatter
group: number_skeletons
# Number skeleton support; separated from numberformatter
- number_skeletons.o number_capi.o number_asformat.o
+ number_skeletons.o number_capi.o number_asformat.o numrange_capi.o
deps
numberformatter
units_extra