]> granicus.if.org Git - icu/commitdiff
ICU-21840 Fix formatting Aliases
authorYounies <younies.mahmoud@gmail.com>
Tue, 15 Mar 2022 20:29:45 +0000 (20:29 +0000)
committerYounies Mahmoud <younies@chromium.org>
Tue, 15 Mar 2022 22:27:43 +0000 (23:27 +0100)
See #2016

icu4c/source/i18n/number_longnames.cpp
icu4c/source/test/intltest/numbertest.h
icu4c/source/test/intltest/numbertest_api.cpp
icu4j/main/classes/core/src/com/ibm/icu/impl/number/LongNameHandler.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java

index 5a4cf6321c8a401f161ea492aa00e9774976f4a8..b4e96504dede98175fef9a3735020ad9322d2d50 100644 (file)
@@ -431,13 +431,33 @@ void getMeasureData(const Locale &locale,
     subKey.append(unit.getType(), status);
     subKey.append("/", status);
 
+    // Check if unitSubType is an alias or not.
+    LocalUResourceBundlePointer aliasBundle(ures_open(U_ICUDATA_ALIAS, "metadata", &status));
+
+    UErrorCode aliasStatus = status;
+    StackUResourceBundle aliasFillIn;
+    CharString aliasKey;
+    aliasKey.append("alias/unit/", aliasStatus);
+    aliasKey.append(unit.getSubtype(), aliasStatus);
+    aliasKey.append("/replacement", aliasStatus);
+    ures_getByKeyWithFallback(aliasBundle.getAlias(), aliasKey.data(), aliasFillIn.getAlias(),
+                              &aliasStatus);
+    CharString unitSubType;
+    if (!U_FAILURE(aliasStatus)) {
+        // This means the subType is an alias. Then, replace unitSubType with the replacement.
+        auto replacement = ures_getUnicodeString(aliasFillIn.getAlias(), &status);
+        unitSubType.appendInvariantChars(replacement, status);
+    } else {
+        unitSubType.append(unit.getSubtype(), status);
+    }
+
     // Map duration-year-person, duration-week-person, etc. to duration-year, duration-week, ...
     // TODO(ICU-20400): Get duration-*-person data properly with aliases.
-    int32_t subtypeLen = static_cast<int32_t>(uprv_strlen(unit.getSubtype()));
-    if (subtypeLen > 7 && uprv_strcmp(unit.getSubtype() + subtypeLen - 7, "-person") == 0) {
-        subKey.append({unit.getSubtype(), subtypeLen - 7}, status);
+    int32_t subtypeLen = static_cast<int32_t>(uprv_strlen(unitSubType.data()));
+    if (subtypeLen > 7 && uprv_strcmp(unitSubType.data() + subtypeLen - 7, "-person") == 0) {
+        subKey.append({unitSubType.data(), subtypeLen - 7}, status);
     } else {
-        subKey.append({unit.getSubtype(), subtypeLen}, status);
+        subKey.append({unitSubType.data(), subtypeLen}, status);
     }
 
     if (width != UNUM_UNIT_WIDTH_FULL_NAME) {
index 7556786c5022271798212b7508b1e21f60adfee6..c0f2e6fd58a5c33d68349343140ef26f652c9f8d 100644 (file)
@@ -101,7 +101,8 @@ class NumberFormatterApiTest : public IntlTestWithFieldPosition {
     void toObject();
     void toDecimalNumber();
     void microPropsInternals();
-
+    void formatUnitsAliases();
+    
     void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0) override;
 
   private:
index a8f7326792eb794b6d93637f0f78897342315d73..b477a16e93e7c90b9f5cdd44f4a23626c08187cf 100644 (file)
@@ -129,6 +129,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha
         TESTCASE_AUTO(toObject);
         TESTCASE_AUTO(toDecimalNumber);
         TESTCASE_AUTO(microPropsInternals);
+        TESTCASE_AUTO(formatUnitsAliases);
     TESTCASE_AUTO_END;
 }
 
@@ -6174,6 +6175,37 @@ void NumberFormatterApiTest::microPropsInternals() {
     assertEquals("Copy Assigned capacity", 4, copyAssigned.mixedMeasures.getCapacity());
 }
 
+void NumberFormatterApiTest::formatUnitsAliases() {
+    IcuTestErrorCode status(*this, "formatUnitsAliases");
+
+    struct TestCase {
+        const MeasureUnit measureUnit;
+        const UnicodeString expectedFormat;
+    } testCases[]{
+        // Aliases
+        {MeasureUnit::getMilligramPerDeciliter(), u"2 milligrams per deciliter"},
+        {MeasureUnit::getLiterPer100Kilometers(), u"2 liters per 100 kilometers"},
+        {MeasureUnit::getPartPerMillion(), u"2 parts per million"},
+        {MeasureUnit::getMillimeterOfMercury(), u"2 millimeters of mercury"},
+
+        // Replacements
+        {MeasureUnit::getMilligramOfglucosePerDeciliter(), u"2 milligrams per deciliter"},
+        {MeasureUnit::forIdentifier("millimeter-ofhg", status), u"2 millimeters of mercury"},
+        {MeasureUnit::forIdentifier("liter-per-100-kilometer", status), u"2 liters per 100 kilometers"},
+        {MeasureUnit::forIdentifier("permillion", status), u"2 parts per million"},
+    };
+
+    for (const auto &testCase : testCases) {
+        UnicodeString actualFormat = NumberFormatter::withLocale(icu::Locale::getEnglish())
+                                         .unit(testCase.measureUnit)
+                                         .unitWidth(UNumberUnitWidth::UNUM_UNIT_WIDTH_FULL_NAME)
+                                         .formatDouble(2.0, status)
+                                         .toString(status);
+
+        assertEquals("test unit aliases", testCase.expectedFormat, actualFormat);
+    }
+}
+
 /* For skeleton comparisons: this checks the toSkeleton output for `f` and for
  * `conciseSkeleton` against the normalized version of `uskeleton` - this does
  * not round-trip uskeleton itself.
index 4f5dc01254f34b44290b6fab66fadcbe3e34d305..6ccc96e711a3635147d0218b36b00d5a74568cd0 100644 (file)
@@ -329,6 +329,21 @@ public class LongNameHandler
         }
     }
 
+    private static final class AliasSink extends UResource.Sink {
+        String replacement;
+
+        @Override
+        public void put(UResource.Key key, UResource.Value value, boolean noFallback) {
+            UResource.Table aliasTable = value.getTable();
+            for (int i = 0; aliasTable.getKeyAndValue(i, key, value); ++i) {
+                String keyString = key.toString();
+                if (keyString.equals("replacement")) {
+                    this.replacement = value.toString();
+                }
+            }
+        }
+    }
+
     // NOTE: outArray MUST have at least ARRAY_LENGTH entries. No bounds checking is performed.
     static void getMeasureData(
             ULocale locale,
@@ -346,12 +361,23 @@ public class LongNameHandler
         subKey.append(unit.getType());
         subKey.append("/");
 
+        // If the unit is an alias, replace it is identifier with the replacement.
+        String unitSubType = unit.getSubtype();
+        ICUResourceBundle metadataResource = (ICUResourceBundle) UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "metadata");
+        AliasSink aliasSink = new AliasSink();
+
+        metadataResource.getAllItemsWithFallbackNoFail("alias/unit/" + unitSubType, aliasSink);
+        if (aliasSink.replacement != null) {
+            unitSubType = aliasSink.replacement;
+        }
+
+
         // Map duration-year-person, duration-week-person, etc. to duration-year, duration-week, ...
         // TODO(ICU-20400): Get duration-*-person data properly with aliases.
-        if (unit.getSubtype() != null && unit.getSubtype().endsWith("-person")) {
-            subKey.append(unit.getSubtype(), 0, unit.getSubtype().length() - 7);
+        if (unitSubType != null && unitSubType.endsWith("-person")) {
+            subKey.append(unitSubType, 0, unitSubType.length() - 7);
         } else {
-            subKey.append(unit.getSubtype());
+            subKey.append(unitSubType);
         }
 
         if (width != UnitWidth.FULL_NAME) {
index 054a5f34d6eaaf167ae29d00479c53b3c4bc3076..e4a8e9d53dbd2839a7ccdc02e186f10404c33bdf 100644 (file)
@@ -5857,6 +5857,45 @@ public class NumberFormatterApiTest extends TestFmwk {
         }
     }
 
+    @Test
+    public void formatUnitsAliases() {
+
+        class TestCase {
+            final MeasureUnit measureUnit;
+            final String expectedFormat;
+
+            TestCase(MeasureUnit measureUnit, String expectedFormat) {
+                this.measureUnit = measureUnit;
+                this.expectedFormat = expectedFormat;
+            }
+        }
+
+        TestCase[] testCases = {
+                // Aliases
+                new TestCase(MeasureUnit.MILLIGRAM_PER_DECILITER, "2 milligrams per deciliter"),
+                new TestCase(MeasureUnit.LITER_PER_100KILOMETERS, "2 liters per 100 kilometers"),
+                new TestCase(MeasureUnit.PART_PER_MILLION, "2 parts per million"),
+                new TestCase(MeasureUnit.MILLIMETER_OF_MERCURY, "2 millimeters of mercury"),
+
+                // Replacements
+                new TestCase(MeasureUnit.MILLIGRAM_OFGLUCOSE_PER_DECILITER, "2 milligrams per deciliter"),
+                new TestCase(MeasureUnit.forIdentifier("millimeter-ofhg"), "2 millimeters of mercury"),
+                new TestCase(MeasureUnit.forIdentifier("liter-per-100-kilometer"), "2 liters per 100 kilometers"),
+                new TestCase(MeasureUnit.forIdentifier("permillion"), "2 parts per million"),
+        };
+
+        for (TestCase testCase : testCases) {
+            String actualFormat = NumberFormatter
+                    .withLocale(ULocale.ENGLISH)
+                    .unit(testCase.measureUnit)
+                    .unitWidth(UnitWidth.FULL_NAME)
+                    .format(2.0)
+                    .toString();
+
+            assertEquals("test unit aliases", testCase.expectedFormat, actualFormat);
+        }
+    }
+
     static void assertFormatDescending(
             String message,
             String skeleton,