]> granicus.if.org Git - icu/commitdiff
ICU-21173 Add support for more currency variants. ICU4C equivalent of…
authorJohn Wilcock <jowilco@microsoft.com>
Fri, 26 Jun 2020 18:02:16 +0000 (18:02 +0000)
committerShane F. Carr <shane@unicode.org>
Fri, 3 Jul 2020 02:51:15 +0000 (04:51 +0200)
See #1184

12 files changed:
icu4c/source/common/ucurr.cpp
icu4c/source/common/unicode/ucurr.h
icu4c/source/i18n/number_currencysymbols.cpp
icu4c/source/i18n/number_currencysymbols.h
icu4c/source/i18n/number_patternmodifier.cpp
icu4c/source/i18n/number_skeletons.cpp
icu4c/source/i18n/number_skeletons.h
icu4c/source/i18n/unicode/unumberformatter.h
icu4c/source/test/intltest/numbertest.h
icu4c/source/test/intltest/numbertest_api.cpp
icu4c/source/test/intltest/numfmtst.cpp
icu4c/source/test/intltest/numfmtst.h

index 5eacc4a99bc03fedb3ad251231dc16f93f68fd87..f64bd07a71ca8293f2049462e01fe6dacf8bb4a2 100644 (file)
@@ -91,6 +91,8 @@ static const char VAR_DELIM = '_';
 // 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 @@ ucurr_getName(const UChar* currency,
     }
 
     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 @@ ucurr_getName(const UChar* currency,
     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);
index f91cc0df7c61ccd60c49c4a2837518b20fd2e429..6666c138272269f10c4c4b1efe3cff4752c11ea9 100644 (file)
@@ -113,7 +113,29 @@ typedef enum UCurrNameStyle {
      *
      * @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
index 4d6fb2cb1d8decc6b856b3b8a890e32302685be9..9208427904cceb6ee7c2f2f6f6f586c82f610efc 100644 (file)
@@ -44,6 +44,16 @@ UnicodeString CurrencySymbols::getNarrowCurrencySymbol(UErrorCode& status) const
     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;
index 9996bf96ae08a12f1d70edf1388520597ec1a6ea..7e38fdf8287e145910a852f8460dff08fbde4ee3 100644 (file)
@@ -31,6 +31,10 @@ class U_I18N_API CurrencySymbols : public UMemory {
 
     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;
index 45602942aefe8e7acc3e74e16339f9554e761771..314e7cb75ee169c86e13161d8045f9fe255b6ad0 100644 (file)
@@ -294,14 +294,20 @@ UnicodeString MutablePatternModifier::getSymbol(AffixPatternType type) const {
         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) {
+            switch (fUnitWidth) {
+            case UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW:
+                return fCurrencySymbols.getNarrowCurrencySymbol(localStatus);
+            case UNumberUnitWidth::UNUM_UNIT_WIDTH_SHORT:
+                return fCurrencySymbols.getCurrencySymbol(localStatus);
+            case UNumberUnitWidth::UNUM_UNIT_WIDTH_ISO_CODE:
                 return fCurrencySymbols.getIntlCurrencySymbol(localStatus);
-            } else if (fUnitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_HIDDEN) {
+            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();
-            } else if (fUnitWidth == UNumberUnitWidth::UNUM_UNIT_WIDTH_NARROW) {
-                return fCurrencySymbols.getNarrowCurrencySymbol(localStatus);
-            } else {
+            default:
                 return fCurrencySymbols.getCurrencySymbol(localStatus);
             }
         }
index 4ba2647986c755ab7e1cde837cc3ca6732ff7c83..66cae73be3cf8572a17c9d581dece3df0c4053c2 100644 (file)
@@ -80,6 +80,8 @@ void U_CALLCONV initNumberSkeletons(UErrorCode& status) {
     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 @@ UNumberUnitWidth stem_to_object::unitWidth(skeleton::StemEnum stem) {
             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 @@ void enum_to_stem_string::unitWidth(UNumberUnitWidth value, UnicodeString& sb) {
         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 @@ skeleton::parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, Se
         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);
index d9b2c0ee0b19abc88cc8bab11fa190a778378878..dd3813e7a65d994bb8f2eca637ff74dd01c7cc7d 100644 (file)
@@ -95,6 +95,8 @@ enum StemEnum {
     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,
index af98ba0027238a5cddcc7882605ffd2e3a1b80ef..d91f1486f8fb22afb15aad12c61592ad0cd479c4 100644 (file)
@@ -147,6 +147,30 @@ typedef enum UNumberUnitWidth {
      */
             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
index cb0be280a9ad9edb8233961937afcbec79a04d21..668885dabe857cc05b58e74ea2945f140d4d53ee 100644 (file)
@@ -95,6 +95,8 @@ class NumberFormatterApiTest : public IntlTestWithFieldPosition {
     CurrencyUnit ESP;
     CurrencyUnit PTE;
     CurrencyUnit RON;
+    CurrencyUnit TWD;
+    CurrencyUnit TRY;
     CurrencyUnit CNY;
 
     MeasureUnit METER;
index c586603b6f0a1b30e85dd2c4441d2946f6b05a93..ad7a2991d98201b6c068f44f67c371e479a7d2b3 100644 (file)
@@ -36,6 +36,8 @@ NumberFormatterApiTest::NumberFormatterApiTest(UErrorCode& status)
           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),
@@ -850,6 +852,42 @@ void NumberFormatterApiTest::unitCurrency() {
             5.43,
             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",
index ed912cf3ce1f1f60e834cd0159ced0ea7fe930da..cd06f6029b2d7674e3870166043be321a188aad4 100644 (file)
@@ -133,7 +133,7 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n
   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 @@ void NumberFormatTest::TestCurrencyNames(void) {
     // 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 @@ void NumberFormatTest::Test20484_NarrowSymbolFallback(){
             &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 @@ void NumberFormatTest::Test20484_NarrowSymbolFallback(){
         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);
     }
 }
 
index 309098c32aa5d3fb86c86708f3fa8b4de6fb5dcf..4e2d797b1076af1d22bd8697164d11e6c5a94ea3 100644 (file)
@@ -153,7 +153,7 @@ class NumberFormatTest: public CalendarTimeZoneTest {
 
     void TestCurrencyNames(void);
 
-    void Test20484_NarrowSymbolFallback(void);
+    void TestCurrencyVariants(void);
 
     void TestCurrencyAmount(void);