ICU-21173 Add support for more currency variants. ICU4C equivalent of…

See #1184
diff --git a/icu4c/source/common/ucurr.cpp b/icu4c/source/common/ucurr.cpp
index 5eacc4a..f64bd07 100644
--- a/icu4c/source/common/ucurr.cpp
+++ b/icu4c/source/common/ucurr.cpp
@@ -91,6 +91,8 @@
 // Tag for localized display names (symbols) of currencies
 static const char CURRENCIES[] = "Currencies";
 static const char CURRENCIES_NARROW[] = "Currencies%narrow";
+static const char CURRENCIES_FORMAL[] = "Currencies%formal";
+static const char CURRENCIES_VARIANT[] = "Currencies%variant";
 static const char CURRENCYPLURALS[] = "CurrencyPlurals";
 
 // ISO codes mapping table
@@ -649,7 +651,7 @@
     }
 
     int32_t choice = (int32_t) nameStyle;
-    if (choice < 0 || choice > 2) {
+    if (choice < 0 || choice > 4) {
         *ec = U_ILLEGAL_ARGUMENT_ERROR;
         return 0;
     }
@@ -684,9 +686,22 @@
     ec2 = U_ZERO_ERROR;
     LocalUResourceBundlePointer rb(ures_open(U_ICUDATA_CURR, loc, &ec2));
 
-    if (nameStyle == UCURR_NARROW_SYMBOL_NAME) {
+    if (nameStyle == UCURR_NARROW_SYMBOL_NAME || nameStyle == UCURR_FORMAL_SYMBOL_NAME || nameStyle == UCURR_VARIANT_SYMBOL_NAME) {
         CharString key;
-        key.append(CURRENCIES_NARROW, ec2);
+        switch (nameStyle) {
+        case UCURR_NARROW_SYMBOL_NAME:
+            key.append(CURRENCIES_NARROW, ec2);
+            break;
+        case UCURR_FORMAL_SYMBOL_NAME:
+            key.append(CURRENCIES_FORMAL, ec2);
+            break;
+        case UCURR_VARIANT_SYMBOL_NAME:
+            key.append(CURRENCIES_VARIANT, ec2);
+            break;
+        default:
+            *ec = U_UNSUPPORTED_ERROR;
+            return 0;
+        }
         key.append("/", ec2);
         key.append(buf, ec2);
         s = ures_getStringByKeyWithFallback(rb.getAlias(), key.data(), len, &ec2);
diff --git a/icu4c/source/common/unicode/ucurr.h b/icu4c/source/common/unicode/ucurr.h
index f91cc0d..6666c13 100644
--- a/icu4c/source/common/unicode/ucurr.h
+++ b/icu4c/source/common/unicode/ucurr.h
@@ -113,7 +113,29 @@
      *
      * @stable ICU 61
      */
-    UCURR_NARROW_SYMBOL_NAME
+    UCURR_NARROW_SYMBOL_NAME,
+
+#ifndef U_HIDE_DRAFT_API
+    /**
+     * Selector for getName() indicating the formal currency symbol.
+     * The formal currency symbol is similar to the regular currency
+     * symbol, but it always takes the form used in formal settings
+     * such as banking; for example, "NT$" instead of "$" for TWD in zh-TW.
+     *
+     * @draft ICU 68
+     */
+    UCURR_FORMAL_SYMBOL_NAME,
+
+    /**
+     * Selector for getName() indicating the variant currency symbol.
+     * The variant symbol for a currency is an alternative symbol
+     * that is not necessarily as widely used as the regular symbol.
+     *
+     * @draft ICU 68
+     */
+    UCURR_VARIANT_SYMBOL_NAME
+#endif  // U_HIDE_DRAFT_API
+    
 } UCurrNameStyle;
 
 #if !UCONFIG_NO_SERVICE
diff --git a/icu4c/source/i18n/number_currencysymbols.cpp b/icu4c/source/i18n/number_currencysymbols.cpp
index 4d6fb2c..9208427 100644
--- a/icu4c/source/i18n/number_currencysymbols.cpp
+++ b/icu4c/source/i18n/number_currencysymbols.cpp
@@ -44,6 +44,16 @@
     return loadSymbol(UCURR_NARROW_SYMBOL_NAME, status);
 }
 
+UnicodeString CurrencySymbols::getFormalCurrencySymbol(UErrorCode& status) const {
+    // Note: currently no override is available for formal currency symbol
+    return loadSymbol(UCURR_FORMAL_SYMBOL_NAME, status);
+}
+
+UnicodeString CurrencySymbols::getVariantCurrencySymbol(UErrorCode& status) const {
+    // Note: currently no override is available for variant currency symbol
+    return loadSymbol(UCURR_VARIANT_SYMBOL_NAME, status);
+}
+
 UnicodeString CurrencySymbols::getCurrencySymbol(UErrorCode& status) const {
     if (!fCurrencySymbol.isBogus()) {
         return fCurrencySymbol;
diff --git a/icu4c/source/i18n/number_currencysymbols.h b/icu4c/source/i18n/number_currencysymbols.h
index 9996bf9..7e38fdf 100644
--- a/icu4c/source/i18n/number_currencysymbols.h
+++ b/icu4c/source/i18n/number_currencysymbols.h
@@ -31,6 +31,10 @@
 
     UnicodeString getNarrowCurrencySymbol(UErrorCode& status) const;
 
+    UnicodeString getFormalCurrencySymbol(UErrorCode& status) const;
+
+    UnicodeString getVariantCurrencySymbol(UErrorCode& status) const;
+
     UnicodeString getCurrencySymbol(UErrorCode& status) const;
 
     UnicodeString getIntlCurrencySymbol(UErrorCode& status) const;
diff --git a/icu4c/source/i18n/number_patternmodifier.cpp b/icu4c/source/i18n/number_patternmodifier.cpp
index 4560294..314e7cb 100644
--- a/icu4c/source/i18n/number_patternmodifier.cpp
+++ b/icu4c/source/i18n/number_patternmodifier.cpp
@@ -294,14 +294,20 @@
         case AffixPatternType::TYPE_PERMILLE:
             return fSymbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kPerMillSymbol);
         case AffixPatternType::TYPE_CURRENCY_SINGLE: {
-            // UnitWidth ISO and HIDDEN overrides the singular currency symbol.
-            if (fUnitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE) {
-                return fCurrencySymbols.getIntlCurrencySymbol(localStatus);
-            } else if (fUnitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN) {
-                return UnicodeString();
-            } else if (fUnitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW) {
+            switch (fUnitWidth) {
+            case UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW:
                 return fCurrencySymbols.getNarrowCurrencySymbol(localStatus);
-            } else {
+            case UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT:
+                return fCurrencySymbols.getCurrencySymbol(localStatus);
+            case UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE:
+                return fCurrencySymbols.getIntlCurrencySymbol(localStatus);
+            case UNumberUnitWidth::UNUM_UNIT_WIDTH_FORMAL:
+                return fCurrencySymbols.getFormalCurrencySymbol(localStatus);
+            case UNumberUnitWidth::UNUM_UNIT_WIDTH_VARIANT:
+                return fCurrencySymbols.getVariantCurrencySymbol(localStatus);
+            case UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN:
+                return UnicodeString();
+            default:
                 return fCurrencySymbols.getCurrencySymbol(localStatus);
             }
         }
diff --git a/icu4c/source/i18n/number_skeletons.cpp b/icu4c/source/i18n/number_skeletons.cpp
index 4ba2647..66cae73 100644
--- a/icu4c/source/i18n/number_skeletons.cpp
+++ b/icu4c/source/i18n/number_skeletons.cpp
@@ -80,6 +80,8 @@
     b.add(u"unit-width-short", STEM_UNIT_WIDTH_SHORT, status);
     b.add(u"unit-width-full-name", STEM_UNIT_WIDTH_FULL_NAME, status);
     b.add(u"unit-width-iso-code", STEM_UNIT_WIDTH_ISO_CODE, status);
+    b.add(u"unit-width-formal", STEM_UNIT_WIDTH_FORMAL, status);
+    b.add(u"unit-width-variant", STEM_UNIT_WIDTH_VARIANT, status);
     b.add(u"unit-width-hidden", STEM_UNIT_WIDTH_HIDDEN, status);
     b.add(u"sign-auto", STEM_SIGN_AUTO, status);
     b.add(u"sign-always", STEM_SIGN_ALWAYS, status);
@@ -265,6 +267,10 @@
             return UNUM_UNIT_WIDTH_FULL_NAME;
         case STEM_UNIT_WIDTH_ISO_CODE:
             return UNUM_UNIT_WIDTH_ISO_CODE;
+        case STEM_UNIT_WIDTH_FORMAL:
+            return UNUM_UNIT_WIDTH_FORMAL;
+        case STEM_UNIT_WIDTH_VARIANT:
+            return UNUM_UNIT_WIDTH_VARIANT;
         case STEM_UNIT_WIDTH_HIDDEN:
             return UNUM_UNIT_WIDTH_HIDDEN;
         default:
@@ -372,6 +378,12 @@
         case UNUM_UNIT_WIDTH_ISO_CODE:
             sb.append(u"unit-width-iso-code", -1);
             break;
+        case UNUM_UNIT_WIDTH_FORMAL:
+            sb.append(u"unit-width-formal", -1);
+            break;
+        case UNUM_UNIT_WIDTH_VARIANT:
+            sb.append(u"unit-width-variant", -1);
+            break;
         case UNUM_UNIT_WIDTH_HIDDEN:
             sb.append(u"unit-width-hidden", -1);
             break;
@@ -683,6 +695,8 @@
         case STEM_UNIT_WIDTH_SHORT:
         case STEM_UNIT_WIDTH_FULL_NAME:
         case STEM_UNIT_WIDTH_ISO_CODE:
+        case STEM_UNIT_WIDTH_FORMAL:
+        case STEM_UNIT_WIDTH_VARIANT:
         case STEM_UNIT_WIDTH_HIDDEN:
             CHECK_NULL(seen, unitWidth, status);
             macros.unitWidth = stem_to_object::unitWidth(stem);
diff --git a/icu4c/source/i18n/number_skeletons.h b/icu4c/source/i18n/number_skeletons.h
index d9b2c0e..dd3813e 100644
--- a/icu4c/source/i18n/number_skeletons.h
+++ b/icu4c/source/i18n/number_skeletons.h
@@ -95,6 +95,8 @@
     STEM_UNIT_WIDTH_SHORT,
     STEM_UNIT_WIDTH_FULL_NAME,
     STEM_UNIT_WIDTH_ISO_CODE,
+    STEM_UNIT_WIDTH_FORMAL,
+    STEM_UNIT_WIDTH_VARIANT,
     STEM_UNIT_WIDTH_HIDDEN,
     STEM_SIGN_AUTO,
     STEM_SIGN_ALWAYS,
diff --git a/icu4c/source/i18n/unicode/unumberformatter.h b/icu4c/source/i18n/unicode/unumberformatter.h
index af98ba0..d91f148 100644
--- a/icu4c/source/i18n/unicode/unumberformatter.h
+++ b/icu4c/source/i18n/unicode/unumberformatter.h
@@ -147,6 +147,30 @@
      */
             UNUM_UNIT_WIDTH_ISO_CODE,
 
+#ifndef U_HIDE_DRAFT_API
+    /**
+     * Use the formal variant of the currency symbol; for example, "NT$" for the New Taiwan
+     * dollar in zh-TW.
+     *
+     * <p>
+     * Behavior of this option with non-currency units is not defined at this time.
+     *
+     * @draft ICU 68
+     */
+            UNUM_UNIT_WIDTH_FORMAL,
+
+    /**
+     * Use the alternate variant of the currency symbol; for example, "TL" for the Turkish
+     * lira (TRY).
+     *
+     * <p>
+     * Behavior of this option with non-currency units is not defined at this time.
+     *
+     * @draft ICU 68
+     */
+            UNUM_UNIT_WIDTH_VARIANT,
+#endif  // U_HIDE_DRAFT_API
+
     /**
      * Format the number according to the specified unit, but do not display the unit. For currencies, apply
      * monetary symbols and formats as with SHORT, but omit the currency symbol. For measure units, the behavior is
diff --git a/icu4c/source/test/intltest/numbertest.h b/icu4c/source/test/intltest/numbertest.h
index cb0be28..668885d 100644
--- a/icu4c/source/test/intltest/numbertest.h
+++ b/icu4c/source/test/intltest/numbertest.h
@@ -95,6 +95,8 @@
     CurrencyUnit ESP;
     CurrencyUnit PTE;
     CurrencyUnit RON;
+    CurrencyUnit TWD;
+    CurrencyUnit TRY;
     CurrencyUnit CNY;
 
     MeasureUnit METER;
diff --git a/icu4c/source/test/intltest/numbertest_api.cpp b/icu4c/source/test/intltest/numbertest_api.cpp
index c586603..ad7a299 100644
--- a/icu4c/source/test/intltest/numbertest_api.cpp
+++ b/icu4c/source/test/intltest/numbertest_api.cpp
@@ -36,6 +36,8 @@
           ESP(u"ESP", status),
           PTE(u"PTE", status),
           RON(u"RON", status),
+          TWD(u"TWD", status),
+          TRY(u"TRY", status),
           CNY(u"CNY", status),
           FRENCH_SYMBOLS(Locale::getFrench(), status),
           SWISS_SYMBOLS(Locale("de-CH"), status),
@@ -851,6 +853,42 @@
             u"US$5.43");
 
     assertFormatSingle(
+            u"Currency Difference between Formal and Short (Formal Version)",
+            u"currency/TWD unit-width-formal",
+            u"currency/TWD unit-width-formal",
+            NumberFormatter::with().unit(TWD).unitWidth(UNUM_UNIT_WIDTH_FORMAL),
+            Locale("zh-TW"),
+            5.43,
+            u"NT$5.43");
+
+    assertFormatSingle(
+            u"Currency Difference between Formal and Short (Short Version)",
+            u"currency/TWD unit-width-short",
+            u"currency/TWD unit-width-short",
+            NumberFormatter::with().unit(TWD).unitWidth(UNUM_UNIT_WIDTH_SHORT),
+            Locale("zh-TW"),
+            5.43,
+            u"$5.43");
+
+    assertFormatSingle(
+            u"Currency Difference between Variant and Short (Formal Version)",
+            u"currency/TRY unit-width-variant",
+            u"currency/TRY unit-width-variant",
+            NumberFormatter::with().unit(TRY).unitWidth(UNUM_UNIT_WIDTH_VARIANT),
+            Locale("tr-TR"),
+            5.43,
+            u"TL\u00A05,43");
+
+    assertFormatSingle(
+            u"Currency Difference between Variant and Short (Short Version)",
+            u"currency/TRY unit-width-short",
+            u"currency/TRY unit-width-short",
+            NumberFormatter::with().unit(TRY).unitWidth(UNUM_UNIT_WIDTH_SHORT),
+            Locale("tr-TR"),
+            5.43,
+            u"₺5,43");
+
+    assertFormatSingle(
             u"Currency-dependent format (Control)",
             u"currency/USD unit-width-short",
             u"currency/USD unit-width-short",
diff --git a/icu4c/source/test/intltest/numfmtst.cpp b/icu4c/source/test/intltest/numfmtst.cpp
index ed912cf..cd06f60 100644
--- a/icu4c/source/test/intltest/numfmtst.cpp
+++ b/icu4c/source/test/intltest/numfmtst.cpp
@@ -133,7 +133,7 @@
   TESTCASE_AUTO(TestCases);
 
   TESTCASE_AUTO(TestCurrencyNames);
-  TESTCASE_AUTO(Test20484_NarrowSymbolFallback);
+  TESTCASE_AUTO(TestCurrencyVariants);
   TESTCASE_AUTO(TestCurrencyAmount);
   TESTCASE_AUTO(TestCurrencyUnit);
   TESTCASE_AUTO(TestCoverage);
@@ -2116,22 +2116,26 @@
     // TODO add more tests later
 }
 
-void NumberFormatTest::Test20484_NarrowSymbolFallback(){
-    IcuTestErrorCode status(*this, "Test20484_NarrowSymbolFallback");
+void NumberFormatTest::TestCurrencyVariants(){
+    IcuTestErrorCode status(*this, "TestCurrencyVariants");
 
     struct TestCase {
         const char* locale;
         const char16_t* isoCode;
         const char16_t* expectedShort;
         const char16_t* expectedNarrow;
+        const char16_t* expectedFormal;
+        const char16_t* expectedVariant;
         UErrorCode expectedNarrowError;
     } cases[] = {
-        {"en-US", u"CAD", u"CA$", u"$", U_USING_DEFAULT_WARNING}, // narrow: fallback to root
-        {"en-US", u"CDF", u"CDF", u"CDF", U_USING_FALLBACK_WARNING}, // narrow: fallback to short
-        {"sw-CD", u"CDF", u"FC", u"FC", U_USING_FALLBACK_WARNING}, // narrow: fallback to short
-        {"en-US", u"GEL", u"GEL", u"₾", U_USING_DEFAULT_WARNING}, // narrow: fallback to root
-        {"ka-GE", u"GEL", u"₾", u"₾", U_USING_FALLBACK_WARNING}, // narrow: fallback to ka
-        {"ka", u"GEL", u"₾", u"₾", U_ZERO_ERROR}, // no fallback on narrow
+        {"en-US", u"CAD", u"CA$", u"$", u"CA$", u"CA$", U_USING_DEFAULT_WARNING}, // narrow: fallback to root
+        {"en-US", u"CDF", u"CDF", u"CDF", u"CDF", u"CDF", U_USING_FALLBACK_WARNING}, // narrow: fallback to short
+        {"sw-CD", u"CDF", u"FC", u"FC", u"FC", u"FC", U_USING_FALLBACK_WARNING}, // narrow: fallback to short
+        {"en-US", u"GEL", u"GEL", u"₾", u"GEL", u"GEL", U_USING_DEFAULT_WARNING}, // narrow: fallback to root
+        {"ka-GE", u"GEL", u"₾", u"₾", u"₾", u"₾", U_USING_FALLBACK_WARNING}, // narrow: fallback to ka
+        {"ka", u"GEL", u"₾", u"₾", u"₾", u"₾", U_ZERO_ERROR}, // no fallback on narrow
+        {"zh-TW", u"TWD", u"$", u"$", u"NT$", u"$", U_USING_FALLBACK_WARNING}, // narrow: fallback to short
+        {"ccp", u"TRY", u"TRY", u"₺", u"TRY", u"TL", U_ZERO_ERROR}, // no fallback on variant
     };
     for (const auto& cas : cases) {
         status.setScope(cas.isoCode);
@@ -2144,6 +2148,20 @@
             &choiceFormatIgnored,
             &lengthIgnored,
             status);
+        const UChar* actualFormal = ucurr_getName(
+            cas.isoCode,
+            cas.locale,
+            UCURR_FORMAL_SYMBOL_NAME,
+            &choiceFormatIgnored,
+            &lengthIgnored,
+            status);
+        const UChar* actualVarant = ucurr_getName(
+            cas.isoCode,
+            cas.locale,
+            UCURR_VARIANT_SYMBOL_NAME,
+            &choiceFormatIgnored,
+            &lengthIgnored,
+            status);
         status.errIfFailureAndReset();
         const UChar* actualNarrow = ucurr_getName(
             cas.isoCode,
@@ -2155,8 +2173,12 @@
         status.expectErrorAndReset(cas.expectedNarrowError);
         assertEquals(UnicodeString("Short symbol: ") + cas.locale + u": " + cas.isoCode,
                 cas.expectedShort, actualShort);
-        assertEquals(UnicodeString("Narrow symbol: ") + cas.locale + ": " + cas.isoCode,
+        assertEquals(UnicodeString("Narrow symbol: ") + cas.locale + u": " + cas.isoCode,
                 cas.expectedNarrow, actualNarrow);
+        assertEquals(UnicodeString("Formal symbol: ") + cas.locale + u": " + cas.isoCode,
+                cas.expectedFormal, actualFormal);
+        assertEquals(UnicodeString("Variant symbol: ") + cas.locale + u": " + cas.isoCode,
+                cas.expectedVariant, actualVarant);
     }
 }
 
diff --git a/icu4c/source/test/intltest/numfmtst.h b/icu4c/source/test/intltest/numfmtst.h
index 309098c..4e2d797 100644
--- a/icu4c/source/test/intltest/numfmtst.h
+++ b/icu4c/source/test/intltest/numfmtst.h
@@ -153,7 +153,7 @@
 
     void TestCurrencyNames(void);
 
-    void Test20484_NarrowSymbolFallback(void);
+    void TestCurrencyVariants(void);
 
     void TestCurrencyAmount(void);