Skip to content

Commit

Permalink
ICU-22781 Support Arbitrary Constant Unit Formatting (C++)
Browse files Browse the repository at this point in the history
  • Loading branch information
younies authored and Squash Bot committed Feb 10, 2025
1 parent f495d10 commit d132924
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 75 deletions.
15 changes: 13 additions & 2 deletions icu4c/source/i18n/measunit_extra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1322,7 +1322,7 @@ void MeasureUnitImpl::serialize(UErrorCode &status) {
return;
}

if (this->singleUnits.length() == 0) {
if (this->singleUnits.length() == 0 && this->constantDenominator == 0) {
// Dimensionless, constructed by the default constructor.
return;
}
Expand Down Expand Up @@ -1497,9 +1497,20 @@ MeasureUnit MeasureUnit::product(const MeasureUnit& other, UErrorCode& status) c
for (int32_t i = 0; i < otherImpl.singleUnits.length(); i++) {
impl.appendSingleUnit(*otherImpl.singleUnits[i], status);
}
if (impl.singleUnits.length() > 1) {

if (this->getConstantDenominator(status) != 0 && other.getConstantDenominator(status) != 0) {
// Cannot multiply units that both of them have a constant denominator
status = U_ILLEGAL_ARGUMENT_ERROR;
return {};
}

impl.constantDenominator =
this->getConstantDenominator(status) + other.getConstantDenominator(status);

if (impl.singleUnits.length() > 1 || impl.constantDenominator > 0) {
impl.complexity = UMEASURE_UNIT_COMPOUND;
}

return std::move(impl).build(status);
}

Expand Down
50 changes: 49 additions & 1 deletion icu4c/source/i18n/number_longnames.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,12 @@ constexpr int32_t PER_INDEX = StandardPlural::Form::COUNT + 1;
* Gender of the word, in languages with grammatical gender.
*/
constexpr int32_t GENDER_INDEX = StandardPlural::Form::COUNT + 2;
/**
* Denominator constant of the unit.
*/
constexpr int32_t CONSTANT_DENOMINATOR_INDEX = StandardPlural::Form::COUNT + 3;
// Number of keys in the array populated by PluralTableSink.
constexpr int32_t ARRAY_LENGTH = StandardPlural::Form::COUNT + 3;
constexpr int32_t ARRAY_LENGTH = StandardPlural::Form::COUNT + 4;

// TODO(icu-units#28): load this list from resources, after creating a "&set"
// function for use in ldml2icu rules.
Expand Down Expand Up @@ -1010,6 +1014,11 @@ void LongNameHandler::forArbitraryUnit(const Locale &loc,
// denominator (the part after the "-per-). If both are empty, fail
MeasureUnitImpl unit;
MeasureUnitImpl perUnit;

if (unitRef.getConstantDenominator(status) != 0) {
perUnit.constantDenominator = unitRef.getConstantDenominator(status);
}

{
MeasureUnitImpl fullUnit = MeasureUnitImpl::forMeasureUnitMaybeCopy(unitRef, status);
if (U_FAILURE(status)) {
Expand Down Expand Up @@ -1196,6 +1205,12 @@ void LongNameHandler::processPatternTimes(MeasureUnitImpl &&productUnit,
DerivedComponents derivedTimesCases(loc, "case", "times");
DerivedComponents derivedPowerCases(loc, "case", "power");

if (productUnit.constantDenominator != 0) {
CharString constantString;
constantString.appendNumber(productUnit.constantDenominator, status);
outArray[CONSTANT_DENOMINATOR_INDEX] = UnicodeString::fromUTF8(constantString.toStringPiece());
}

// 4. For each single_unit in product_unit
for (int32_t singleUnitIndex = 0; singleUnitIndex < productUnit.singleUnits.length();
singleUnitIndex++) {
Expand Down Expand Up @@ -1454,6 +1469,39 @@ void LongNameHandler::processPatternTimes(MeasureUnitImpl &&productUnit,
}
}
}

// 5. Handling constant denominator if it exists.
if (productUnit.constantDenominator != 0) {
int32_t pluralIndex = -1;
for (int32_t index = 0; index < StandardPlural::Form::COUNT; index++) {
if (!outArray[index].isBogus()) {
pluralIndex = index;
break;
}
}

U_ASSERT(pluralIndex >= 0); // "No plural form found for constant denominator"

// TODO(ICU-23039):
// Improve the handling of constant_denominator representation.
// For instance, a constant_denominator of 1000000 should be adaptable to
// formats like
// 1,000,000, 1e6, or 1 million.
// Furthermore, ensure consistent pluralization rules for units. For example,
// "meter per 100 seconds" should be evaluated for correct singular/plural
// usage: "second" or "seconds"?
// Similarly, "kilogram per 1000 meters" should be checked for "meter" or
// "meters"?
if (outArray[pluralIndex].length() == 0) {
outArray[pluralIndex] = outArray[CONSTANT_DENOMINATOR_INDEX];
} else {
UnicodeString tmp;
timesPatternFormatter.format(outArray[CONSTANT_DENOMINATOR_INDEX], outArray[pluralIndex],
tmp, status);
outArray[pluralIndex] = tmp;
}
}

for (int32_t pluralIndex = 0; pluralIndex < StandardPlural::Form::COUNT; pluralIndex++) {
if (globalPlaceholder[pluralIndex] == PH_BEGINNING) {
UnicodeString tmp;
Expand Down
1 change: 1 addition & 0 deletions icu4c/source/test/intltest/numbertest.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class NumberFormatterApiTest : public IntlTestWithFieldPosition {
void toDecimalNumber();
void microPropsInternals();
void formatUnitsAliases();
void formatArbitraryConstant();
void testIssue22378();

void runIndexedTest(int32_t index, UBool exec, const char*& name, char* par = nullptr) override;
Expand Down
Loading

0 comments on commit d132924

Please sign in to comment.