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 @@ 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);
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 @@ 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
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 @@ 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;
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 @@ 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;
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 @@ 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) {
- 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 @@ 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);
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 @@ 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,
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 @@ 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
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 @@ class NumberFormatterApiTest : public IntlTestWithFieldPosition {
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 @@ 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),
@@ -851,6 +853,42 @@ void NumberFormatterApiTest::unitCurrency() {
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 @@ 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);
}
}
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 @@ class NumberFormatTest: public CalendarTimeZoneTest {
void TestCurrencyNames(void);
- void Test20484_NarrowSymbolFallback(void);
+ void TestCurrencyVariants(void);
void TestCurrencyAmount(void);