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) {
void toObject();
void toDecimalNumber();
void microPropsInternals();
-
+ void formatUnitsAliases();
+
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0) override;
private:
TESTCASE_AUTO(toObject);
TESTCASE_AUTO(toDecimalNumber);
TESTCASE_AUTO(microPropsInternals);
+ TESTCASE_AUTO(formatUnitsAliases);
TESTCASE_AUTO_END;
}
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.
}
}
+ 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,
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) {
}
}
+ @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,