@@ -67,12 +67,11 @@ int getTimeZonesForLocale(char *localeId, const char **values);
6767
6868} // namespace
6969
70- ecma402_locale *
71- ecma402_applyLocaleOptions (ecma402_locale *locale, const char *calendar,
72- const char *caseFirst, const char *collation,
73- const char *hourCycle, const char *language,
74- const char *numberingSystem, int numeric,
75- const char *region, const char *script) {
70+ ecma402_locale *ecma402_applyLocaleOptions (
71+ ecma402_locale *locale, const char *calendar, const char *caseFirst,
72+ const char *collation, const char *currency, const char *hourCycle,
73+ const char *language, const char *numberingSystem, int numeric,
74+ const char *region, const char *script) {
7675 icu::Locale icuLocale;
7776 icu::LocaleBuilder icuLocaleBuilder;
7877 UErrorCode icuStatus = U_ZERO_ERROR;
@@ -98,6 +97,10 @@ ecma402_applyLocaleOptions(ecma402_locale *locale, const char *calendar,
9897 collation);
9998 }
10099
100+ if (currency != nullptr ) {
101+ icuLocaleBuilder.setUnicodeLocaleKeyword (BCP47_KEYWORD_CURRENCY, currency);
102+ }
103+
101104 if (hourCycle != nullptr ) {
102105 icuLocaleBuilder.setUnicodeLocaleKeyword (BCP47_KEYWORD_HOUR_CYCLE,
103106 hourCycle);
@@ -261,6 +264,7 @@ void ecma402_freeLocale(ecma402_locale *locale) {
261264 FREE_PROPERTY (canonical);
262265 FREE_PROPERTY (caseFirst);
263266 FREE_PROPERTY (collation);
267+ FREE_PROPERTY (currency);
264268 FREE_PROPERTY (hourCycle);
265269 FREE_PROPERTY (language);
266270 FREE_PROPERTY (numberingSystem);
@@ -326,6 +330,70 @@ int ecma402_getCollation(const char *localeId, char *collation,
326330 isCanonicalized);
327331}
328332
333+ int ecma402_getCurrency (const char *localeId, char *currency,
334+ ecma402_errorStatus *status, bool isCanonicalized) {
335+ char *canonicalized;
336+ UChar buffer[4 ];
337+ UErrorCode icuStatus = U_ZERO_ERROR;
338+ std::string icuValue;
339+ int icuValueLength;
340+
341+ if (localeId == nullptr ) {
342+ return -1 ;
343+ }
344+
345+ if (isCanonicalized) {
346+ canonicalized = strdup (localeId);
347+ } else {
348+ canonicalized = (char *)malloc (sizeof (char ) * ULOC_FULLNAME_CAPACITY);
349+ ecma402_canonicalizeUnicodeLocaleId (localeId, canonicalized, status);
350+
351+ if (ecma402_hasError (status)) {
352+ free (canonicalized);
353+ return -1 ;
354+ }
355+ }
356+
357+ // If given a locale like "en-US-u-cu-foobar," ucurr_forLocale() will return
358+ // the default currency for the locale (e.g., "USD"), and if given a locale
359+ // like "en-US-u-cu-fo," it will return "YES," but we would prefer it to
360+ // indicate it couldn't find a default currency, since the user provided one,
361+ // so we do this check here to see if the "cu" user-provided value is exactly
362+ // 3 alphanumeric characters. If it is not, we return -1.
363+ std::string const canonicalStr (canonicalized);
364+ free (canonicalized);
365+
366+ size_t const cuPos = canonicalStr.find (" -cu-" );
367+ if (cuPos != std::string::npos) {
368+ size_t const startPos = cuPos + 4 ;
369+ size_t const endPos = canonicalStr.find (' -' , startPos);
370+ size_t const cuLen =
371+ (endPos == std::string::npos) ? std::string::npos : endPos - startPos;
372+
373+ std::string const cuStr = canonicalStr.substr (startPos, cuLen);
374+ if (cuStr.length () != 3 ) {
375+ return -1 ;
376+ }
377+ } else {
378+ // The locale does not specify a currency.
379+ return -1 ;
380+ }
381+
382+ icuValueLength = ucurr_forLocale (canonicalStr.c_str (), buffer, 4 , &icuStatus);
383+
384+ if (U_FAILURE (icuStatus) != U_ZERO_ERROR) {
385+ return -1 ;
386+ }
387+
388+ for (int i = 0 ; i < icuValueLength; i++) {
389+ icuValue.push_back (buffer[i]);
390+ }
391+
392+ memcpy (currency, icuValue.c_str (), icuValue.length () + 1 );
393+
394+ return icuValue.length ();
395+ }
396+
329397int ecma402_getHourCycle (const char *localeId, char *hourCycle,
330398 ecma402_errorStatus *status, bool isCanonicalized) {
331399 return getKeywordValue (ICU_KEYWORD_HOUR_CYCLE, localeId, hourCycle, status,
@@ -371,6 +439,7 @@ ecma402_locale *ecma402_initEmptyLocale(void) {
371439 locale->canonical = nullptr ;
372440 locale->caseFirst = nullptr ;
373441 locale->collation = nullptr ;
442+ locale->currency = nullptr ;
374443 locale->hourCycle = nullptr ;
375444 locale->language = nullptr ;
376445 locale->numberingSystem = nullptr ;
@@ -412,6 +481,7 @@ ecma402_locale *ecma402_initLocale(const char *localeId) {
412481 INIT_PROPERTY (canonical, calendar, ULOC_KEYWORDS_CAPACITY, getCalendar);
413482 INIT_PROPERTY (canonical, caseFirst, ULOC_KEYWORDS_CAPACITY, getCaseFirst);
414483 INIT_PROPERTY (canonical, collation, ULOC_KEYWORDS_CAPACITY, getCollation);
484+ INIT_PROPERTY (canonical, currency, 4 , getCurrency);
415485 INIT_PROPERTY (canonical, hourCycle, ULOC_KEYWORDS_CAPACITY, getHourCycle);
416486 INIT_PROPERTY (canonical, language, ULOC_LANG_CAPACITY, getLanguage);
417487 INIT_PROPERTY (canonical, numberingSystem, ULOC_KEYWORDS_CAPACITY,
@@ -468,8 +538,7 @@ int ecma402_keywordsOfLocale(ecma402_locale *locale, const char *keyword,
468538
469539 canonical = locale->canonical ;
470540
471- if (strcmp (keyword, ICU_KEYWORD_TIME_ZONE) == 0 ||
472- strcmp (keyword, ICU_KEYWORD_CURRENCY) == 0 ) {
541+ if (strcmp (keyword, ICU_KEYWORD_TIME_ZONE) == 0 ) {
473542 // Skip checking for a "preferred" identifier for these keywords.
474543 } else {
475544 // Check to see whether the localeId already has the keyword value set on
@@ -483,7 +552,14 @@ int ecma402_keywordsOfLocale(ecma402_locale *locale, const char *keyword,
483552 return 0 ;
484553 }
485554
486- if (preferredLength > 0 ) {
555+ // If the keyword is "currency," there's some special handling: it must have
556+ // a length of exactly 3, and it must not have a value of "YES" (which
557+ // implies the length was actually less than 3 and ICU converted that to a
558+ // truthy string "YES").
559+ // If the keyword is not "currency," then the length must be greater than 0.
560+ if ((strcmp (keyword, ICU_KEYWORD_CURRENCY) == 0 && preferredLength == 3 &&
561+ strcasecmp (preferred, " YES" ) != 0 ) ||
562+ (strcmp (keyword, ICU_KEYWORD_CURRENCY) != 0 && preferredLength > 0 )) {
487563 values[0 ] = strdup (uloc_toUnicodeLocaleType (keyword, preferred));
488564 free (preferred);
489565 return 1 ;
0 commit comments