From: Shane Carr Date: Thu, 15 Sep 2016 08:37:56 +0000 (+0000) Subject: ICU-12029 Measure unit display names, C++ version. X-Git-Tag: milestone-59-0-1~205 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5b9fc6fc70d29c04c0178c85746b428c19c6afa8;p=icu ICU-12029 Measure unit display names, C++ version. X-SVN-Rev: 39243 --- diff --git a/icu4c/source/i18n/measfmt.cpp b/icu4c/source/i18n/measfmt.cpp index 4ad5a29564b..61618ab44d0 100644 --- a/icu4c/source/i18n/measfmt.cpp +++ b/icu4c/source/i18n/measfmt.cpp @@ -110,6 +110,7 @@ public: UMeasureFormatWidth widthFallback[WIDTH_INDEX_COUNT]; /** Measure unit -> format width -> array of patterns ("{0} meters") (plurals + PER_UNIT_INDEX) */ SimpleFormatter *patterns[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT][PATTERN_COUNT]; + const UChar* dnams[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT]; SimpleFormatter perFormatters[WIDTH_INDEX_COUNT]; MeasureFormatCacheData(); @@ -159,6 +160,7 @@ MeasureFormatCacheData::MeasureFormatCacheData() { currencyFormats[i] = NULL; } uprv_memset(patterns, 0, sizeof(patterns)); + uprv_memset(dnams, 0, sizeof(dnams)); integerFormat = NULL; numericDateFormatters = NULL; } @@ -174,6 +176,7 @@ MeasureFormatCacheData::~MeasureFormatCacheData() { } } } + // Note: the contents of 'dnams' are pointers into the resource bundle delete integerFormat; delete numericDateFormatters; } @@ -232,17 +235,25 @@ struct UnitDataSink : public ResourceSink { void setFormatterIfAbsent(int32_t index, const ResourceValue &value, int32_t minPlaceholders, UErrorCode &errorCode) { - SimpleFormatter **patterns = - &cacheData.patterns[unitIndex][width][0]; + SimpleFormatter **patterns = &cacheData.patterns[unitIndex][width][0]; if (U_SUCCESS(errorCode) && patterns[index] == NULL) { - patterns[index] = new SimpleFormatter( - value.getUnicodeString(errorCode), minPlaceholders, 1, errorCode); + if (minPlaceholders >= 0) { + patterns[index] = new SimpleFormatter( + value.getUnicodeString(errorCode), minPlaceholders, 1, errorCode); + } if (U_SUCCESS(errorCode) && patterns[index] == NULL) { errorCode = U_MEMORY_ALLOCATION_ERROR; } } } + void setDnamIfAbsent(const ResourceValue &value, UErrorCode& errorCode) { + if (cacheData.dnams[unitIndex][width] == NULL) { + int32_t length; + cacheData.dnams[unitIndex][width] = value.getString(length, errorCode); + } + } + /** * Consume a display pattern. For example, * unitsShort/duration/hour contains other{"{0} hrs"}. @@ -250,7 +261,8 @@ struct UnitDataSink : public ResourceSink { void consumePattern(const char *key, const ResourceValue &value, UErrorCode &errorCode) { if (U_FAILURE(errorCode)) { return; } if (uprv_strcmp(key, "dnam") == 0) { - // Skip the unit display name for now. + // The display name for the unit in the current width. + setDnamIfAbsent(value, errorCode); } else if (uprv_strcmp(key, "per") == 0) { // For example, "{0}/h". setFormatterIfAbsent(MeasureFormatCacheData::PER_UNIT_INDEX, value, 1, errorCode); @@ -817,6 +829,24 @@ UnicodeString &MeasureFormat::formatMeasures( return appendTo; } +UnicodeString MeasureFormat::getUnitDisplayName(const MeasureUnit& unit, UErrorCode& /*status*/) const { + UMeasureFormatWidth width = getRegularWidth(this->width); + const UChar* const* styleToDnam = cache->dnams[unit.getIndex()]; + const UChar* dnam = styleToDnam[width]; + if (dnam == NULL) { + int32_t fallbackWidth = cache->widthFallback[width]; + dnam = styleToDnam[fallbackWidth]; + } + + UnicodeString result; + if (dnam == NULL) { + result.setToBogus(); + } else { + result.setTo(dnam, -1); + } + return result; +} + void MeasureFormat::initMeasureFormat( const Locale &locale, UMeasureFormatWidth w, diff --git a/icu4c/source/i18n/unicode/measfmt.h b/icu4c/source/i18n/unicode/measfmt.h index a6d8cb81b5e..de545f16013 100644 --- a/icu4c/source/i18n/unicode/measfmt.h +++ b/icu4c/source/i18n/unicode/measfmt.h @@ -210,6 +210,19 @@ class U_I18N_API MeasureFormat : public Format { FieldPosition &pos, UErrorCode &status) const; + /** + * Gets the display name of the specified {@link MeasureUnit} corresponding to the current + * locale and format width. + * @param unit The unit for which to get a display name. + * @param status the error. + * @return The display name in the locale and width specified in + * {@link MeasureFormat#getInstance}, or null if there is no display name available + * for the specified unit. + * + * @draft ICU 58 + */ + UnicodeString getUnitDisplayName(const MeasureUnit& unit, UErrorCode &status) const; + /** * Return a formatter for CurrencyAmount objects in the given diff --git a/icu4c/source/test/intltest/measfmttest.cpp b/icu4c/source/test/intltest/measfmttest.cpp index 4728bc4bb3d..7bd0b52cfed 100644 --- a/icu4c/source/test/intltest/measfmttest.cpp +++ b/icu4c/source/test/intltest/measfmttest.cpp @@ -24,6 +24,7 @@ #include "unicode/tmunit.h" #include "unicode/plurrule.h" #include "charstr.h" +#include "cstr.h" #include "unicode/reldatefmt.h" struct ExpectedResult { @@ -58,6 +59,7 @@ private: void TestManyLocaleDurations(); void TestGram(); void TestCurrencies(); + void TestDisplayNames(); void TestFieldPosition(); void TestFieldPositionMultiple(); void TestBadArg(); @@ -111,6 +113,11 @@ private: const Measure *measures, int32_t measureCount, const char *expected); + void helperTestDisplayName( + const MeasureUnit *unit, + const char *localeID, + UMeasureFormatWidth width, + const char *expected); void verifyFieldPosition( const char *description, const MeasureFormat &fmt, @@ -147,6 +154,7 @@ void MeasureFormatTest::runIndexedTest( TESTCASE_AUTO(TestManyLocaleDurations); TESTCASE_AUTO(TestGram); TESTCASE_AUTO(TestCurrencies); + TESTCASE_AUTO(TestDisplayNames); TESTCASE_AUTO(TestFieldPosition); TESTCASE_AUTO(TestFieldPositionMultiple); TESTCASE_AUTO(TestBadArg); @@ -1592,6 +1600,61 @@ void MeasureFormatTest::TestCurrencies() { verifyFormat("TestCurrenciesNumeric", fmt, &USD_2, 1, "$2.00"); } +void MeasureFormatTest::TestDisplayNames() { + UErrorCode status = U_ZERO_ERROR; + helperTestDisplayName( MeasureUnit::createYear(status), "en", UMEASFMT_WIDTH_WIDE, "years" ); + helperTestDisplayName( MeasureUnit::createYear(status), "ja", UMEASFMT_WIDTH_WIDE, "\\u5E74" ); + helperTestDisplayName( MeasureUnit::createYear(status), "es", UMEASFMT_WIDTH_WIDE, "a\\u00F1os" ); + helperTestDisplayName( MeasureUnit::createYear(status), "pt", UMEASFMT_WIDTH_WIDE, "anos" ); + helperTestDisplayName( MeasureUnit::createYear(status), "pt-PT", UMEASFMT_WIDTH_WIDE, "anos" ); + helperTestDisplayName( MeasureUnit::createAmpere(status), "en", UMEASFMT_WIDTH_WIDE, "amperes" ); + helperTestDisplayName( MeasureUnit::createAmpere(status), "ja", UMEASFMT_WIDTH_WIDE, "\\u30A2\\u30F3\\u30DA\\u30A2" ); + helperTestDisplayName( MeasureUnit::createAmpere(status), "es", UMEASFMT_WIDTH_WIDE, "amperios" ); + helperTestDisplayName( MeasureUnit::createAmpere(status), "pt", UMEASFMT_WIDTH_WIDE, "amperes" ); + helperTestDisplayName( MeasureUnit::createAmpere(status), "pt-PT", UMEASFMT_WIDTH_WIDE, "amperes" ); + helperTestDisplayName( MeasureUnit::createMeterPerSecondSquared(status), "pt", UMEASFMT_WIDTH_WIDE, "metros por segundo ao quadrado" ); + helperTestDisplayName( MeasureUnit::createMeterPerSecondSquared(status), "pt-PT", UMEASFMT_WIDTH_WIDE, "metros por segundo quadrado" ); + helperTestDisplayName( MeasureUnit::createSquareKilometer(status), "pt", UMEASFMT_WIDTH_NARROW, "km\\u00B2" ); + helperTestDisplayName( MeasureUnit::createSquareKilometer(status), "pt", UMEASFMT_WIDTH_SHORT, "km\\u00B2" ); + helperTestDisplayName( MeasureUnit::createSquareKilometer(status), "pt", UMEASFMT_WIDTH_WIDE, "quil\\u00F4metros quadrados" ); + helperTestDisplayName( MeasureUnit::createSecond(status), "pt-PT", UMEASFMT_WIDTH_NARROW, "s" ); + helperTestDisplayName( MeasureUnit::createSecond(status), "pt-PT", UMEASFMT_WIDTH_SHORT, "s" ); + helperTestDisplayName( MeasureUnit::createSecond(status), "pt-PT", UMEASFMT_WIDTH_WIDE, "segundos" ); + helperTestDisplayName( MeasureUnit::createSecond(status), "pt", UMEASFMT_WIDTH_NARROW, "seg" ); + helperTestDisplayName( MeasureUnit::createSecond(status), "pt", UMEASFMT_WIDTH_SHORT, "segs" ); + helperTestDisplayName( MeasureUnit::createSecond(status), "pt", UMEASFMT_WIDTH_WIDE, "segundos" ); + assertSuccess("Error creating measure units", status); +} + +void MeasureFormatTest::helperTestDisplayName(const MeasureUnit *unit, + const char *localeID, + UMeasureFormatWidth width, + const char *expected) { + UErrorCode status = U_ZERO_ERROR; + MeasureFormat fmt(Locale(localeID), width, status); + if (U_FAILURE(status)) { + errln("Could not create MeasureFormat for locale %s, width %d, status: %s", + localeID, (int)width, u_errorName(status)); + return; + } + + UnicodeString dnam = fmt.getUnitDisplayName(*unit, status); + if (U_FAILURE(status)) { + errln("MeasureFormat::getUnitDisplayName failed for unit %s-%s, locale %s, width %d, status: %s", + unit->getType(), unit->getSubtype(), localeID, (int)width, u_errorName(status)); + return; + } + + UnicodeString expStr(UnicodeString(expected).unescape()); + if (dnam != expStr) { + errln("MeasureFormat::getUnitDisplayName for unit %s-%s, locale %s, width %d: expected \"%s\", got \"%s\"", + unit->getType(), unit->getSubtype(), localeID, (int)width, CStr(expStr)(), CStr(dnam)()); + } + + // Delete the measure unit + delete unit; +} + void MeasureFormatTest::TestFieldPosition() { UErrorCode status = U_ZERO_ERROR; MeasureFormat fmt("en", UMEASFMT_WIDTH_SHORT, status);