return fData->outputUnit;
}
+NounClass FormattedNumber::getNounClass(UErrorCode &status) const {
+ UPRV_FORMATTED_VALUE_METHOD_GUARD(NounClass::OTHER);
+ const char *nounClass = fData->gender;
+
+ // if it is not exist, return `OTHER`
+ if (uprv_strcmp(nounClass, "") == 0) {
+ return NounClass::OTHER;
+ }
+
+ if (uprv_strcmp(nounClass, "neuter") == 0) {
+ return NounClass::NEUTER;
+ }
+
+ if (uprv_strcmp(nounClass, "feminine") == 0) {
+ return NounClass::FEMININE;
+ }
+
+ if (uprv_strcmp(nounClass, "masculine") == 0) {
+ return NounClass::MASCULINE;
+ }
+
+ if (uprv_strcmp(nounClass, "animate") == 0) {
+ return NounClass::ANIMATE;
+ }
+
+ if (uprv_strcmp(nounClass, "inanimate") == 0) {
+ return NounClass::INANIMATE;
+ }
+
+ if (uprv_strcmp(nounClass, "personal") == 0) {
+ return NounClass::PERSONAL;
+ }
+
+ if (uprv_strcmp(nounClass, "common") == 0) {
+ return NounClass::COMMON;
+ }
+
+ // In case there is no matching, this means there are noun classes
+ // that are not supported yet.
+ status = U_INTERNAL_PROGRAM_ERROR;
+ return NounClass::OTHER;
+}
+
const char *FormattedNumber::getGender(UErrorCode &status) const {
UPRV_FORMATTED_VALUE_METHOD_GUARD("")
return fData->gender;
#include "unicode/parseerr.h"
#include "unicode/plurrule.h"
#include "unicode/ucurr.h"
+#include "unicode/unounclass.h"
#include "unicode/unum.h"
#include "unicode/unumberformatter.h"
#include "unicode/uobject.h"
*/
MeasureUnit getOutputUnit(UErrorCode& status) const;
-#ifndef U_HIDE_INTERNAL_API
+#ifndef U_HIDE_DRAFT_API
+
/**
- * Gets the gender of the formatted output. Returns "" when the gender is
- * unknown, or for ungendered languages.
+ * Gets the noun class of the formatted output. Returns `OTHER` when the noun class
+ * is not supported yet.
*
- * @internal ICU 69 technology preview.
+ * @return `NounClass`
+ * @draft ICU 71.
*/
- const char *getGender(UErrorCode& status) const;
+ NounClass getNounClass(UErrorCode &status) const;
+
+#endif // U_HIDE_DRAFT_API
+
+#ifndef U_HIDE_INTERNAL_API
/**
* Gets the raw DecimalQuantity for plural rule selection.
#endif /* U_HIDE_INTERNAL_API */
+#ifndef U_HIDE_DEPRECATED_API
+
+ /**
+ * Gets the gender of the formatted output. Returns "" when the gender is
+ * unknown, or for ungendered languages.
+ *
+ * @deprecated This API is for ICU internal use only.
+ */
+ const char *getGender(UErrorCode &status) const;
+
+#endif /* U_HIDE_DEPRECATED_API */
+
private:
// Can't use LocalPointer because UFormattedNumberData is forward-declared
const impl::UFormattedNumberData *fData;
--- /dev/null
+// © 2022 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+#ifndef __UNOUNCLASS_H__
+#define __UNOUNCLASS_H__
+
+#include "unicode/utypes.h"
+
+#if U_SHOW_CPLUSPLUS_API
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/uversion.h"
+
+U_NAMESPACE_BEGIN
+
+#ifndef U_HIDE_DRAFT_API
+
+/**
+ * Represents all the grammatical noun classes that are supported by CLDR.
+ *
+ * @draft ICU 71.
+ */
+enum NounClass {
+ OTHER = 0,
+ NEUTER = 1,
+ FEMININE = 2,
+ MASCULINE = 3,
+ ANIMATE = 4,
+ INANIMATE = 5,
+ PERSONAL = 6,
+ COMMON = 7,
+};
+
+#endif // U_HIDE_DRAFT_API
+
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
+
+#endif /* U_SHOW_CPLUSPLUS_API */
+
+#endif // __UNOUNCLASS_H__
void unitUsageSkeletons();
void unitCurrency();
void unitInflections();
+ void unitNounClass();
void unitGender();
void unitNotConvertible();
void unitPercent();
TESTCASE_AUTO(unitUsageSkeletons);
TESTCASE_AUTO(unitCurrency);
TESTCASE_AUTO(unitInflections);
+ TESTCASE_AUTO(unitNounClass);
TESTCASE_AUTO(unitGender);
TESTCASE_AUTO(unitNotConvertible);
TESTCASE_AUTO(unitPercent);
// TODO: look at "↑↑↑" cases: check that inheritance is done right.
}
+using icu::NounClass;
+void NumberFormatterApiTest::unitNounClass() {
+ IcuTestErrorCode status(*this, "unitNounClass");
+ const struct TestCase {
+ const char *locale;
+ const char *unitIdentifier;
+ const NounClass expectedNounClass;
+ } cases[] = {
+ {"de", "inch", NounClass::MASCULINE},
+ {"de", "yard", NounClass::NEUTER},
+ {"de", "meter", NounClass::MASCULINE},
+ {"de", "liter", NounClass::MASCULINE},
+ {"de", "second", NounClass::FEMININE},
+ {"de", "minute", NounClass::FEMININE},
+ {"de", "hour", NounClass::FEMININE},
+ {"de", "day", NounClass::MASCULINE},
+ {"de", "year", NounClass::NEUTER},
+ {"de", "gram", NounClass::NEUTER},
+ {"de", "watt", NounClass::NEUTER},
+ {"de", "bit", NounClass::NEUTER},
+ {"de", "byte", NounClass::NEUTER},
+
+ {"fr", "inch", NounClass::MASCULINE},
+ {"fr", "yard", NounClass::MASCULINE},
+ {"fr", "meter", NounClass::MASCULINE},
+ {"fr", "liter", NounClass::MASCULINE},
+ {"fr", "second", NounClass::FEMININE},
+ {"fr", "minute", NounClass::FEMININE},
+ {"fr", "hour", NounClass::FEMININE},
+ {"fr", "day", NounClass::MASCULINE},
+ {"fr", "year", NounClass::MASCULINE},
+ {"fr", "gram", NounClass::MASCULINE},
+
+ // grammaticalFeatures deriveCompound "per" rule takes the gender of the
+ // numerator unit:
+ {"de", "meter-per-hour", NounClass::MASCULINE},
+ {"fr", "meter-per-hour", NounClass::MASCULINE},
+ {"af", "meter-per-hour", NounClass::OTHER}, // ungendered language
+
+ // French "times" takes gender from first value, German takes the
+ // second. Prefix and power does not have impact on gender for these
+ // languages:
+ {"de", "square-decimeter-square-second", NounClass::FEMININE},
+ {"fr", "square-decimeter-square-second", NounClass::MASCULINE},
+
+ // TODO(icu-units#149): percent and permille bypasses LongNameHandler
+ // when unitWidth is not FULL_NAME:
+ // // Gender of per-second might be that of percent? TODO(icu-units#28)
+ // {"de", "percent", NounClass::NEUTER},
+ // {"fr", "percent", NounClass::MASCULINE},
+
+ // Built-in units whose simple units lack gender in the CLDR data file
+ {"de", "kilopascal", NounClass::NEUTER},
+ {"fr", "kilopascal", NounClass::MASCULINE},
+ // {"de", "pascal", NounClass::OTHER},
+ // {"fr", "pascal", NounClass::OTHER},
+
+ // Built-in units that lack gender in the CLDR data file
+ // {"de", "revolution", NounClass::OTHER},
+ // {"de", "radian", NounClass::OTHER},
+ // {"de", "arc-minute", NounClass::OTHER},
+ // {"de", "arc-second", NounClass::OTHER},
+ {"de", "square-yard", NounClass::NEUTER}, // POWER
+ {"de", "square-inch", NounClass::MASCULINE}, // POWER
+ // {"de", "dunam", NounClass::OTHER},
+ // {"de", "karat", NounClass::OTHER},
+ // {"de", "milligram-ofglucose-per-deciliter", NounClass::OTHER}, // COMPOUND, ofglucose
+ // {"de", "millimole-per-liter", NounClass::OTHER}, // COMPOUND, mole
+ // {"de", "permillion", NounClass::OTHER},
+ // {"de", "permille", NounClass::OTHER},
+ // {"de", "permyriad", NounClass::OTHER},
+ // {"de", "mole", NounClass::OTHER},
+ {"de", "liter-per-kilometer", NounClass::MASCULINE}, // COMPOUND
+ {"de", "petabyte", NounClass::NEUTER}, // PREFIX
+ {"de", "terabit", NounClass::NEUTER}, // PREFIX
+ // {"de", "century", NounClass::OTHER},
+ // {"de", "decade", NounClass::OTHER},
+ {"de", "millisecond", NounClass::FEMININE}, // PREFIX
+ {"de", "microsecond", NounClass::FEMININE}, // PREFIX
+ {"de", "nanosecond", NounClass::FEMININE}, // PREFIX
+ // {"de", "ampere", NounClass::OTHER},
+ // {"de", "milliampere", NounClass::OTHER}, // PREFIX, ampere
+ // {"de", "ohm", NounClass::OTHER},
+ // {"de", "calorie", NounClass::OTHER},
+ // {"de", "kilojoule", NounClass::OTHER}, // PREFIX, joule
+ // {"de", "joule", NounClass::OTHER},
+ {"de", "kilowatt-hour", NounClass::FEMININE}, // COMPOUND
+ // {"de", "electronvolt", NounClass::OTHER},
+ // {"de", "british-thermal-unit", NounClass::OTHER},
+ // {"de", "therm-us", NounClass::OTHER},
+ // {"de", "pound-force", NounClass::OTHER},
+ // {"de", "newton", NounClass::OTHER},
+ // {"de", "gigahertz", NounClass::OTHER}, // PREFIX, hertz
+ // {"de", "megahertz", NounClass::OTHER}, // PREFIX, hertz
+ // {"de", "kilohertz", NounClass::OTHER}, // PREFIX, hertz
+ // {"de", "hertz", NounClass::OTHER},
+ // {"de", "em", NounClass::OTHER},
+ // {"de", "pixel", NounClass::OTHER},
+ // {"de", "megapixel", NounClass::OTHER},
+ // {"de", "pixel-per-centimeter", NounClass::OTHER}, // COMPOUND, pixel
+ // {"de", "pixel-per-inch", NounClass::OTHER}, // COMPOUND, pixel
+ // {"de", "dot-per-centimeter", NounClass::OTHER}, // COMPOUND, dot
+ // {"de", "dot-per-inch", NounClass::OTHER}, // COMPOUND, dot
+ // {"de", "dot", NounClass::OTHER},
+ // {"de", "earth-radius", NounClass::OTHER},
+ {"de", "decimeter", NounClass::MASCULINE}, // PREFIX
+ {"de", "micrometer", NounClass::MASCULINE}, // PREFIX
+ {"de", "nanometer", NounClass::MASCULINE}, // PREFIX
+ // {"de", "light-year", NounClass::OTHER},
+ // {"de", "astronomical-unit", NounClass::OTHER},
+ // {"de", "furlong", NounClass::OTHER},
+ // {"de", "fathom", NounClass::OTHER},
+ // {"de", "nautical-mile", NounClass::OTHER},
+ // {"de", "mile-scandinavian", NounClass::OTHER},
+ // {"de", "point", NounClass::OTHER},
+ // {"de", "lux", NounClass::OTHER},
+ // {"de", "candela", NounClass::OTHER},
+ // {"de", "lumen", NounClass::OTHER},
+ // {"de", "metric-ton", NounClass::OTHER},
+ // {"de", "microgram", NounClass::NEUTER}, // PREFIX
+ // {"de", "ton", NounClass::OTHER},
+ // {"de", "stone", NounClass::OTHER},
+ // {"de", "ounce-troy", NounClass::OTHER},
+ // {"de", "carat", NounClass::OTHER},
+ {"de", "gigawatt", NounClass::NEUTER}, // PREFIX
+ {"de", "milliwatt", NounClass::NEUTER}, // PREFIX
+ // {"de", "horsepower", NounClass::OTHER},
+ // {"de", "millimeter-ofhg", NounClass::OTHER},
+ // {"de", "pound-force-per-square-inch", NounClass::OTHER}, // COMPOUND, pound-force
+ // {"de", "inch-ofhg", NounClass::OTHER},
+ // {"de", "bar", NounClass::OTHER},
+ // {"de", "millibar", NounClass::OTHER}, // PREFIX, bar
+ // {"de", "atmosphere", NounClass::OTHER},
+ // {"de", "pascal", NounClass::OTHER}, // PREFIX, kilopascal? neuter?
+ // {"de", "hectopascal", NounClass::OTHER}, // PREFIX, pascal, neuter?
+ // {"de", "megapascal", NounClass::OTHER}, // PREFIX, pascal, neuter?
+ // {"de", "knot", NounClass::OTHER},
+ {"de", "pound-force-foot", NounClass::MASCULINE}, // COMPOUND
+ {"de", "newton-meter", NounClass::MASCULINE}, // COMPOUND
+ {"de", "cubic-kilometer", NounClass::MASCULINE}, // POWER
+ {"de", "cubic-yard", NounClass::NEUTER}, // POWER
+ {"de", "cubic-inch", NounClass::MASCULINE}, // POWER
+ {"de", "megaliter", NounClass::MASCULINE}, // PREFIX
+ {"de", "hectoliter", NounClass::MASCULINE}, // PREFIX
+ // {"de", "pint-metric", NounClass::OTHER},
+ // {"de", "cup-metric", NounClass::OTHER},
+ {"de", "acre-foot", NounClass::MASCULINE}, // COMPOUND
+ // {"de", "bushel", NounClass::OTHER},
+ // {"de", "barrel", NounClass::OTHER},
+ // Units missing gender in German also misses gender in French:
+ // {"fr", "revolution", NounClass::OTHER},
+ // {"fr", "radian", NounClass::OTHER},
+ // {"fr", "arc-minute", NounClass::OTHER},
+ // {"fr", "arc-second", NounClass::OTHER},
+ {"fr", "square-yard", NounClass::MASCULINE}, // POWER
+ {"fr", "square-inch", NounClass::MASCULINE}, // POWER
+ // {"fr", "dunam", NounClass::OTHER},
+ // {"fr", "karat", NounClass::OTHER},
+ {"fr", "milligram-ofglucose-per-deciliter", NounClass::MASCULINE}, // COMPOUND
+ // {"fr", "millimole-per-liter", NounClass::OTHER}, // COMPOUND, mole
+ // {"fr", "permillion", NounClass::OTHER},
+ // {"fr", "permille", NounClass::OTHER},
+ // {"fr", "permyriad", NounClass::OTHER},
+ // {"fr", "mole", NounClass::OTHER},
+ {"fr", "liter-per-kilometer", NounClass::MASCULINE}, // COMPOUND
+ // {"fr", "petabyte", NounClass::OTHER}, // PREFIX
+ // {"fr", "terabit", NounClass::OTHER}, // PREFIX
+ // {"fr", "century", NounClass::OTHER},
+ // {"fr", "decade", NounClass::OTHER},
+ {"fr", "millisecond", NounClass::FEMININE}, // PREFIX
+ {"fr", "microsecond", NounClass::FEMININE}, // PREFIX
+ {"fr", "nanosecond", NounClass::FEMININE}, // PREFIX
+ // {"fr", "ampere", NounClass::OTHER},
+ // {"fr", "milliampere", NounClass::OTHER}, // PREFIX, ampere
+ // {"fr", "ohm", NounClass::OTHER},
+ // {"fr", "calorie", NounClass::OTHER},
+ // {"fr", "kilojoule", NounClass::OTHER}, // PREFIX, joule
+ // {"fr", "joule", NounClass::OTHER},
+ // {"fr", "kilowatt-hour", NounClass::OTHER}, // COMPOUND
+ // {"fr", "electronvolt", NounClass::OTHER},
+ // {"fr", "british-thermal-unit", NounClass::OTHER},
+ // {"fr", "therm-us", NounClass::OTHER},
+ // {"fr", "pound-force", NounClass::OTHER},
+ // {"fr", "newton", NounClass::OTHER},
+ // {"fr", "gigahertz", NounClass::OTHER}, // PREFIX, hertz
+ // {"fr", "megahertz", NounClass::OTHER}, // PREFIX, hertz
+ // {"fr", "kilohertz", NounClass::OTHER}, // PREFIX, hertz
+ // {"fr", "hertz", NounClass::OTHER},
+ // {"fr", "em", NounClass::OTHER},
+ // {"fr", "pixel", NounClass::OTHER},
+ // {"fr", "megapixel", NounClass::OTHER},
+ // {"fr", "pixel-per-centimeter", NounClass::OTHER}, // COMPOUND, pixel
+ // {"fr", "pixel-per-inch", NounClass::OTHER}, // COMPOUND, pixel
+ // {"fr", "dot-per-centimeter", NounClass::OTHER}, // COMPOUND, dot
+ // {"fr", "dot-per-inch", NounClass::OTHER}, // COMPOUND, dot
+ // {"fr", "dot", NounClass::OTHER},
+ // {"fr", "earth-radius", NounClass::OTHER},
+ {"fr", "decimeter", NounClass::MASCULINE}, // PREFIX
+ {"fr", "micrometer", NounClass::MASCULINE}, // PREFIX
+ {"fr", "nanometer", NounClass::MASCULINE}, // PREFIX
+ // {"fr", "light-year", NounClass::OTHER},
+ // {"fr", "astronomical-unit", NounClass::OTHER},
+ // {"fr", "furlong", NounClass::OTHER},
+ // {"fr", "fathom", NounClass::OTHER},
+ // {"fr", "nautical-mile", NounClass::OTHER},
+ // {"fr", "mile-scandinavian", NounClass::OTHER},
+ // {"fr", "point", NounClass::OTHER},
+ // {"fr", "lux", NounClass::OTHER},
+ // {"fr", "candela", NounClass::OTHER},
+ // {"fr", "lumen", NounClass::OTHER},
+ // {"fr", "metric-ton", NounClass::OTHER},
+ // {"fr", "microgram", NounClass::MASCULINE}, // PREFIX
+ // {"fr", "ton", NounClass::OTHER},
+ // {"fr", "stone", NounClass::OTHER},
+ // {"fr", "ounce-troy", NounClass::OTHER},
+ // {"fr", "carat", NounClass::OTHER},
+ // {"fr", "gigawatt", NounClass::OTHER}, // PREFIX
+ // {"fr", "milliwatt", NounClass::OTHER},
+ // {"fr", "horsepower", NounClass::OTHER},
+ {"fr", "millimeter-ofhg", NounClass::MASCULINE},
+ // {"fr", "pound-force-per-square-inch", NounClass::OTHER}, // COMPOUND, pound-force
+ {"fr", "inch-ofhg", NounClass::MASCULINE},
+ // {"fr", "bar", NounClass::OTHER},
+ // {"fr", "millibar", NounClass::OTHER}, // PREFIX, bar
+ // {"fr", "atmosphere", NounClass::OTHER},
+ // {"fr", "pascal", NounClass::OTHER}, // PREFIX, kilopascal?
+ // {"fr", "hectopascal", NounClass::OTHER}, // PREFIX, pascal
+ // {"fr", "megapascal", NounClass::OTHER}, // PREFIX, pascal
+ // {"fr", "knot", NounClass::OTHER},
+ // {"fr", "pound-force-foot", NounClass::OTHER},
+ // {"fr", "newton-meter", NounClass::OTHER},
+ {"fr", "cubic-kilometer", NounClass::MASCULINE}, // POWER
+ {"fr", "cubic-yard", NounClass::MASCULINE}, // POWER
+ {"fr", "cubic-inch", NounClass::MASCULINE}, // POWER
+ {"fr", "megaliter", NounClass::MASCULINE}, // PREFIX
+ {"fr", "hectoliter", NounClass::MASCULINE}, // PREFIX
+ // {"fr", "pint-metric", NounClass::OTHER},
+ // {"fr", "cup-metric", NounClass::OTHER},
+ {"fr", "acre-foot", NounClass::FEMININE}, // COMPOUND
+ // {"fr", "bushel", NounClass::OTHER},
+ // {"fr", "barrel", NounClass::OTHER},
+ // Some more French units missing gender:
+ // {"fr", "degree", NounClass::OTHER},
+ {"fr", "square-meter", NounClass::MASCULINE}, // POWER
+ // {"fr", "terabyte", NounClass::OTHER}, // PREFIX, byte
+ // {"fr", "gigabyte", NounClass::OTHER}, // PREFIX, byte
+ // {"fr", "gigabit", NounClass::OTHER}, // PREFIX, bit
+ // {"fr", "megabyte", NounClass::OTHER}, // PREFIX, byte
+ // {"fr", "megabit", NounClass::OTHER}, // PREFIX, bit
+ // {"fr", "kilobyte", NounClass::OTHER}, // PREFIX, byte
+ // {"fr", "kilobit", NounClass::OTHER}, // PREFIX, bit
+ // {"fr", "byte", NounClass::OTHER},
+ // {"fr", "bit", NounClass::OTHER},
+ // {"fr", "volt", NounClass::OTHER},
+ // {"fr", "watt", NounClass::OTHER},
+ {"fr", "cubic-meter", NounClass::MASCULINE}, // POWER
+
+ // gender-lacking builtins within compound units
+ {"de", "newton-meter-per-second", NounClass::MASCULINE},
+
+ // TODO(ICU-21494): determine whether list genders behave as follows,
+ // and implement proper getListGender support (covering more than just
+ // two genders):
+ // // gender rule for lists of people: de "neutral", fr "maleTaints"
+ // {"de", "day-and-hour-and-minute", NounClass::NEUTER},
+ // {"de", "hour-and-minute", NounClass::FEMININE},
+ // {"fr", "day-and-hour-and-minute", NounClass::MASCULINE},
+ // {"fr", "hour-and-minute", NounClass::FEMININE},
+ };
+
+ LocalizedNumberFormatter formatter;
+ FormattedNumber fn;
+ for (const TestCase &t : cases) {
+ formatter = NumberFormatter::with()
+ .unit(MeasureUnit::forIdentifier(t.unitIdentifier, status))
+ .locale(Locale(t.locale));
+ fn = formatter.formatDouble(1.1, status);
+ assertEquals(UnicodeString("Testing NounClass with default width, unit: ") + t.unitIdentifier +
+ ", locale: " + t.locale,
+ t.expectedNounClass, fn.getNounClass(status));
+ status.assertSuccess();
+
+ formatter = NumberFormatter::with()
+ .unit(MeasureUnit::forIdentifier(t.unitIdentifier, status))
+ .unitWidth(UNUM_UNIT_WIDTH_FULL_NAME)
+ .locale(Locale(t.locale));
+ fn = formatter.formatDouble(1.1, status);
+ assertEquals(UnicodeString("Testing NounClass with UNUM_UNIT_WIDTH_FULL_NAME, unit: ") +
+ t.unitIdentifier + ", locale: " + t.locale,
+ t.expectedNounClass, fn.getNounClass(status));
+ status.assertSuccess();
+ }
+
+ // Make sure getNounClass does not return garbage for languages without noun classes.
+ formatter = NumberFormatter::with().locale(Locale::getEnglish());
+ fn = formatter.formatDouble(1.1, status);
+ status.assertSuccess();
+ assertEquals("getNounClasses for a not supported language", NounClass::OTHER,
+ fn.getNounClass(status));
+}
+
void NumberFormatterApiTest::unitGender() {
IcuTestErrorCode status(*this, "unitGender");
import com.ibm.icu.text.FormattedValue;
import com.ibm.icu.text.PluralRules.IFixedDecimal;
import com.ibm.icu.util.MeasureUnit;
+import com.ibm.icu.util.NounClass;
+import com.ibm.icu.util.UResourceTypeMismatchException;
/**
* The result of a number formatting operation. This class allows the result to be exported in several
return this.outputUnit;
}
+ /**
+ * Gets the noun class of the formatted output. Returns `OTHER` when the noun class
+ * is not supported yet.
+ *
+ * @throws UResourceTypeMismatchException
+ * @return `NounClass`
+ * @draft ICU 71.
+ */
+ public NounClass getNounClass() {
+ // if it is not exist, return `OTHER`
+ if (this.gender == null || this.gender.isEmpty()) {
+ return NounClass.OTHER;
+ }
+
+ if (this.gender.equals("neuter")) {
+ return NounClass.NEUTER;
+ }
+
+ if (this.gender.equals("feminine")) {
+ return NounClass.FEMININE;
+ }
+
+ if (this.gender.equals("masculine")) {
+ return NounClass.MASCULINE;
+ }
+
+ if (this.gender.equals("animate")) {
+ return NounClass.ANIMATE;
+ }
+
+ if (this.gender.equals("inanimate")) {
+ return NounClass.INANIMATE;
+ }
+
+ if (this.gender.equals("personal")) {
+ return NounClass.PERSONAL;
+ }
+
+ if (this.gender.equals("common")) {
+ return NounClass.COMMON;
+ }
+
+ // In case there is no matching.
+ throw new UResourceTypeMismatchException("there are noun classes that are not supported yet");
+ }
+
/**
* The gender of the formatted output.
*
* @internal ICU 69 technology preview
- * @deprecated This API is for technology preview only.
+ * @deprecated This API is for ICU internal use only.
*/
@Deprecated
public String getGender() {
--- /dev/null
+// © 2022 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html
+
+package com.ibm.icu.util;
+
+/**
+ * Represents all the grammatical noun classes that are supported by CLDR.
+ *
+ * @draft ICU 71.
+ */
+public enum NounClass {
+ OTHER,
+ NEUTER,
+ FEMININE,
+ MASCULINE,
+ ANIMATE,
+ INANIMATE,
+ PERSONAL,
+ COMMON,
+}
import com.ibm.icu.util.CurrencyAmount;
import com.ibm.icu.util.Measure;
import com.ibm.icu.util.MeasureUnit;
+import com.ibm.icu.util.NounClass;
import com.ibm.icu.util.NoUnit;
import com.ibm.icu.util.ULocale;
// TODO: look at "↑↑↑" cases: check that inheritance is done right.
}
+ @Test
+ public void unitNounClass() {
+ class TestCase {
+ public String locale;
+ public String unitIdentifier;
+ public NounClass expectedNounClass;
+
+ public TestCase(String locale, String unitIdentifier, NounClass expectedNounClass) {
+ this.locale = locale;
+ this.unitIdentifier = unitIdentifier;
+ this.expectedNounClass = expectedNounClass;
+ }
+ }
+
+ TestCase cases[] = {
+ new TestCase("de", "inch", NounClass.MASCULINE), //
+ new TestCase("de", "yard", NounClass.NEUTER), //
+ new TestCase("de", "meter", NounClass.MASCULINE), //
+ new TestCase("de", "liter", NounClass.MASCULINE), //
+ new TestCase("de", "second", NounClass.FEMININE), //
+ new TestCase("de", "minute", NounClass.FEMININE), //
+ new TestCase("de", "hour", NounClass.FEMININE), //
+ new TestCase("de", "day", NounClass.MASCULINE), //
+ new TestCase("de", "year", NounClass.NEUTER), //
+ new TestCase("de", "gram", NounClass.NEUTER), //
+ new TestCase("de", "watt", NounClass.NEUTER), //
+ new TestCase("de", "bit", NounClass.NEUTER), //
+ new TestCase("de", "byte", NounClass.NEUTER), //
+
+ new TestCase("fr", "inch", NounClass.MASCULINE), //
+ new TestCase("fr", "yard", NounClass.MASCULINE), //
+ new TestCase("fr", "meter", NounClass.MASCULINE), //
+ new TestCase("fr", "liter", NounClass.MASCULINE), //
+ new TestCase("fr", "second", NounClass.FEMININE), //
+ new TestCase("fr", "minute", NounClass.FEMININE), //
+ new TestCase("fr", "hour", NounClass.FEMININE), //
+ new TestCase("fr", "day", NounClass.MASCULINE), //
+ new TestCase("fr", "year", NounClass.MASCULINE), //
+ new TestCase("fr", "gram", NounClass.MASCULINE), //
+
+ // grammaticalFeatures deriveCompound "per" rule takes the gender of the
+ // numerator unit:
+ new TestCase("de", "meter-per-hour", NounClass.MASCULINE),
+ new TestCase("fr", "meter-per-hour", NounClass.MASCULINE),
+ new TestCase("af", "meter-per-hour", NounClass.OTHER), // ungendered language
+
+ // French "times" takes gender from first value, German takes the
+ // second. Prefix and power does not have impact on gender for these
+ // languages:
+ new TestCase("de", "square-decimeter-square-second", NounClass.FEMININE),
+ new TestCase("fr", "square-decimeter-square-second", NounClass.MASCULINE),
+
+ // TODO(icu-units#149): percent and permille bypasses
+ // LongNameHandler when unitWidth is not FULL_NAME:
+ // // Gender of per-second might be that of percent? TODO(icu-units#28)
+ // new TestCase("de", "percent", NounClass.NEUTER), //
+ // new TestCase("fr", "percent", NounClass.MASCULINE), //
+
+ // Built-in units whose simple units lack gender in the CLDR data file
+ new TestCase("de", "kilopascal", NounClass.NEUTER), //
+ new TestCase("fr", "kilopascal", NounClass.MASCULINE), //
+ // new TestCase("de", "pascal", NounClass.OTHER), //
+ // new TestCase("fr", "pascal", NounClass.OTHER), //
+
+ // Built-in units that lack gender in the CLDR data file
+ // new TestCase("de", "revolution", NounClass.OTHER), //
+ // new TestCase("de", "radian", NounClass.OTHER), //
+ // new TestCase("de", "arc-minute", NounClass.OTHER), //
+ // new TestCase("de", "arc-second", NounClass.OTHER), //
+ new TestCase("de", "square-yard", NounClass.NEUTER), // COMPOUND
+ new TestCase("de", "square-inch", NounClass.MASCULINE), // COMPOUND
+ // new TestCase("de", "dunam", NounClass.OTHER), //
+ // new TestCase("de", "karat", NounClass.OTHER), //
+ // new TestCase("de", "milligram-ofglucose-per-deciliter", NounClass.OTHER), // COMPOUND, ofglucose
+ // new TestCase("de", "millimole-per-liter", NounClass.OTHER), // COMPOUND, mole
+ // new TestCase("de", "permillion", NounClass.OTHER), //
+ // new TestCase("de", "permille", NounClass.OTHER), //
+ // new TestCase("de", "permyriad", NounClass.OTHER), //
+ // new TestCase("de", "mole", NounClass.OTHER), //
+ new TestCase("de", "liter-per-kilometer", NounClass.MASCULINE), // COMPOUND
+ new TestCase("de", "petabyte", NounClass.NEUTER), // PREFIX
+ new TestCase("de", "terabit", NounClass.NEUTER), // PREFIX
+ // new TestCase("de", "century", NounClass.OTHER), //
+ // new TestCase("de", "decade", NounClass.OTHER), //
+ new TestCase("de", "millisecond", NounClass.FEMININE), // PREFIX
+ new TestCase("de", "microsecond", NounClass.FEMININE), // PREFIX
+ new TestCase("de", "nanosecond", NounClass.FEMININE), // PREFIX
+ // new TestCase("de", "ampere", NounClass.OTHER), //
+ // new TestCase("de", "milliampere", NounClass.OTHER), // PREFIX, ampere
+ // new TestCase("de", "ohm", NounClass.OTHER), //
+ // new TestCase("de", "calorie", NounClass.OTHER), //
+ // new TestCase("de", "kilojoule", NounClass.OTHER), // PREFIX, joule
+ // new TestCase("de", "joule", NounClass.OTHER), //
+ new TestCase("de", "kilowatt-hour", NounClass.FEMININE), // COMPOUND
+ // new TestCase("de", "electronvolt", NounClass.OTHER), //
+ // new TestCase("de", "british-thermal-unit", NounClass.OTHER), //
+ // new TestCase("de", "therm-us", NounClass.OTHER), //
+ // new TestCase("de", "pound-force", NounClass.OTHER), //
+ // new TestCase("de", "newton", NounClass.OTHER), //
+ // new TestCase("de", "gigahertz", NounClass.OTHER), // PREFIX, hertz
+ // new TestCase("de", "megahertz", NounClass.OTHER), // PREFIX, hertz
+ // new TestCase("de", "kilohertz", NounClass.OTHER), // PREFIX, hertz
+ // new TestCase("de", "hertz", NounClass.OTHER), // PREFIX, hertz
+ // new TestCase("de", "em", NounClass.OTHER), //
+ // new TestCase("de", "pixel", NounClass.OTHER), //
+ // new TestCase("de", "megapixel", NounClass.OTHER), //
+ // new TestCase("de", "pixel-per-centimeter", NounClass.OTHER), // COMPOUND, pixel
+ // new TestCase("de", "pixel-per-inch", NounClass.OTHER), // COMPOUND, pixel
+ // new TestCase("de", "dot-per-centimeter", NounClass.OTHER), // COMPOUND, dot
+ // new TestCase("de", "dot-per-inch", NounClass.OTHER), // COMPOUND, dot
+ // new TestCase("de", "dot", NounClass.OTHER), //
+ // new TestCase("de", "earth-radius", NounClass.OTHER), //
+ new TestCase("de", "decimeter", NounClass.MASCULINE), // PREFIX
+ new TestCase("de", "micrometer", NounClass.MASCULINE), // PREFIX
+ new TestCase("de", "nanometer", NounClass.MASCULINE), // PREFIX
+ // new TestCase("de", "light-year", NounClass.OTHER), //
+ // new TestCase("de", "astronomical-unit", NounClass.OTHER), //
+ // new TestCase("de", "furlong", NounClass.OTHER), //
+ // new TestCase("de", "fathom", NounClass.OTHER), //
+ // new TestCase("de", "nautical-mile", NounClass.OTHER), //
+ // new TestCase("de", "mile-scandinavian", NounClass.OTHER), //
+ // new TestCase("de", "point", NounClass.OTHER), //
+ // new TestCase("de", "lux", NounClass.OTHER), //
+ // new TestCase("de", "candela", NounClass.OTHER), //
+ // new TestCase("de", "lumen", NounClass.OTHER), //
+ // new TestCase("de", "metric-ton", NounClass.OTHER), //
+ new TestCase("de", "microgram", NounClass.NEUTER), // PREFIX
+ // new TestCase("de", "ton", NounClass.OTHER), //
+ // new TestCase("de", "stone", NounClass.OTHER), //
+ // new TestCase("de", "ounce-troy", NounClass.OTHER), //
+ // new TestCase("de", "carat", NounClass.OTHER), //
+ new TestCase("de", "gigawatt", NounClass.NEUTER), // PREFIX
+ new TestCase("de", "milliwatt", NounClass.NEUTER), // PREFIX
+ // new TestCase("de", "horsepower", NounClass.OTHER), //
+ // new TestCase("de", "millimeter-ofhg", NounClass.OTHER), //
+ // new TestCase("de", "pound-force-per-square-inch", NounClass.OTHER), // COMPOUND, pound-force
+ // new TestCase("de", "inch-ofhg", NounClass.OTHER), //
+ // new TestCase("de", "bar", NounClass.OTHER), //
+ // new TestCase("de", "millibar", NounClass.OTHER), // PREFIX, bar
+ // new TestCase("de", "atmosphere", NounClass.OTHER), //
+ // new TestCase("de", "pascal", NounClass.OTHER), // PREFIX, kilopascal? neuter?
+ // new TestCase("de", "hectopascal", NounClass.OTHER), // PREFIX, pascal, neuter?
+ // new TestCase("de", "megapascal", NounClass.OTHER), // PREFIX, pascal, neuter?
+ // new TestCase("de", "knot", NounClass.OTHER), //
+ new TestCase("de", "pound-force-foot", NounClass.MASCULINE), // COMPOUND
+ new TestCase("de", "newton-meter", NounClass.MASCULINE), // COMPOUND
+ new TestCase("de", "cubic-kilometer", NounClass.MASCULINE), // POWER
+ new TestCase("de", "cubic-yard", NounClass.NEUTER), // POWER
+ new TestCase("de", "cubic-inch", NounClass.MASCULINE), // POWER
+ new TestCase("de", "megaliter", NounClass.MASCULINE), // PREFIX
+ new TestCase("de", "hectoliter", NounClass.MASCULINE), // PREFIX
+ // new TestCase("de", "pint-metric", NounClass.OTHER), //
+ // new TestCase("de", "cup-metric", NounClass.OTHER), //
+ new TestCase("de", "acre-foot", NounClass.MASCULINE), // COMPOUND
+ // new TestCase("de", "bushel", NounClass.OTHER), //
+ // new TestCase("de", "barrel", NounClass.OTHER), //
+ // Units missing gender in German also misses gender in French:
+ // new TestCase("fr", "revolution", NounClass.OTHER), //
+ // new TestCase("fr", "radian", NounClass.OTHER), //
+ // new TestCase("fr", "arc-minute", NounClass.OTHER), //
+ // new TestCase("fr", "arc-second", NounClass.OTHER), //
+ new TestCase("fr", "square-yard", NounClass.MASCULINE), // COMPOUND
+ new TestCase("fr", "square-inch", NounClass.MASCULINE), // COMPOUND
+ // new TestCase("fr", "dunam", NounClass.OTHER), //
+ // new TestCase("fr", "karat", NounClass.OTHER), //
+ new TestCase("fr", "milligram-ofglucose-per-deciliter", NounClass.MASCULINE), // COMPOUND
+ // new TestCase("fr", "millimole-per-liter", NounClass.OTHER), // COMPOUND, mole
+ // new TestCase("fr", "permillion", NounClass.OTHER), //
+ // new TestCase("fr", "permille", NounClass.OTHER), //
+ // new TestCase("fr", "permyriad", NounClass.OTHER), //
+ // new TestCase("fr", "mole", NounClass.OTHER), //
+ new TestCase("fr", "liter-per-kilometer", NounClass.MASCULINE), // COMPOUND
+ // new TestCase("fr", "petabyte", NounClass.OTHER), // PREFIX
+ // new TestCase("fr", "terabit", NounClass.OTHER), // PREFIX
+ // new TestCase("fr", "century", NounClass.OTHER), //
+ // new TestCase("fr", "decade", NounClass.OTHER), //
+ new TestCase("fr", "millisecond", NounClass.FEMININE), // PREFIX
+ new TestCase("fr", "microsecond", NounClass.FEMININE), // PREFIX
+ new TestCase("fr", "nanosecond", NounClass.FEMININE), // PREFIX
+ // new TestCase("fr", "ampere", NounClass.OTHER), //
+ // new TestCase("fr", "milliampere", NounClass.OTHER), // PREFIX, ampere
+ // new TestCase("fr", "ohm", NounClass.OTHER), //
+ // new TestCase("fr", "calorie", NounClass.OTHER), //
+ // new TestCase("fr", "kilojoule", NounClass.OTHER), // PREFIX, joule
+ // new TestCase("fr", "joule", NounClass.OTHER), //
+ // new TestCase("fr", "kilowatt-hour", NounClass.OTHER), // COMPOUND
+ // new TestCase("fr", "electronvolt", NounClass.OTHER), //
+ // new TestCase("fr", "british-thermal-unit", NounClass.OTHER), //
+ // new TestCase("fr", "therm-us", NounClass.OTHER), //
+ // new TestCase("fr", "pound-force", NounClass.OTHER), //
+ // new TestCase("fr", "newton", NounClass.OTHER), //
+ // new TestCase("fr", "gigahertz", NounClass.OTHER), // PREFIX, hertz
+ // new TestCase("fr", "megahertz", NounClass.OTHER), // PREFIX, hertz
+ // new TestCase("fr", "kilohertz", NounClass.OTHER), // PREFIX, hertz
+ // new TestCase("fr", "hertz", NounClass.OTHER), // PREFIX, hertz
+ // new TestCase("fr", "em", NounClass.OTHER), //
+ // new TestCase("fr", "pixel", NounClass.OTHER), //
+ // new TestCase("fr", "megapixel", NounClass.OTHER), //
+ // new TestCase("fr", "pixel-per-centimeter", NounClass.OTHER), // COMPOUND, pixel
+ // new TestCase("fr", "pixel-per-inch", NounClass.OTHER), // COMPOUND, pixel
+ // new TestCase("fr", "dot-per-centimeter", NounClass.OTHER), // COMPOUND, dot
+ // new TestCase("fr", "dot-per-inch", NounClass.OTHER), // COMPOUND, dot
+ // new TestCase("fr", "dot", NounClass.OTHER), //
+ // new TestCase("fr", "earth-radius", NounClass.OTHER), //
+ new TestCase("fr", "decimeter", NounClass.MASCULINE), // PREFIX
+ new TestCase("fr", "micrometer", NounClass.MASCULINE), // PREFIX
+ new TestCase("fr", "nanometer", NounClass.MASCULINE), // PREFIX
+ // new TestCase("fr", "light-year", NounClass.OTHER), //
+ // new TestCase("fr", "astronomical-unit", NounClass.OTHER), //
+ // new TestCase("fr", "furlong", NounClass.OTHER), //
+ // new TestCase("fr", "fathom", NounClass.OTHER), //
+ // new TestCase("fr", "nautical-mile", NounClass.OTHER), //
+ // new TestCase("fr", "mile-scandinavian", NounClass.OTHER), //
+ // new TestCase("fr", "point", NounClass.OTHER), //
+ // new TestCase("fr", "lux", NounClass.OTHER), //
+ // new TestCase("fr", "candela", NounClass.OTHER), //
+ // new TestCase("fr", "lumen", NounClass.OTHER), //
+ // new TestCase("fr", "metric-ton", NounClass.OTHER), //
+ new TestCase("fr", "microgram", NounClass.MASCULINE), // PREFIX
+ // new TestCase("fr", "ton", NounClass.OTHER), //
+ // new TestCase("fr", "stone", NounClass.OTHER), //
+ // new TestCase("fr", "ounce-troy", NounClass.OTHER), //
+ // new TestCase("fr", "carat", NounClass.OTHER), //
+ // new TestCase("fr", "gigawatt", NounClass.OTHER), // PREFIX
+ // new TestCase("fr", "milliwatt", NounClass.OTHER), //
+ // new TestCase("fr", "horsepower", NounClass.OTHER), //
+ new TestCase("fr", "millimeter-ofhg", NounClass.MASCULINE), //
+ // new TestCase("fr", "pound-force-per-square-inch", NounClass.OTHER), // COMPOUND, pound-force
+ new TestCase("fr", "inch-ofhg", NounClass.MASCULINE), //
+ // new TestCase("fr", "bar", NounClass.OTHER), //
+ // new TestCase("fr", "millibar", NounClass.OTHER), // PREFIX, bar
+ // new TestCase("fr", "atmosphere", NounClass.OTHER), //
+ // new TestCase("fr", "pascal", NounClass.OTHER), // PREFIX, kilopascal?
+ // new TestCase("fr", "hectopascal", NounClass.OTHER), // PREFIX, pascal
+ // new TestCase("fr", "megapascal", NounClass.OTHER), // PREFIX, pascal
+ // new TestCase("fr", "knot", NounClass.OTHER), //
+ // new TestCase("fr", "pound-force-foot", NounClass.OTHER), //
+ // new TestCase("fr", "newton-meter", NounClass.OTHER), //
+ new TestCase("fr", "cubic-kilometer", NounClass.MASCULINE), // POWER
+ new TestCase("fr", "cubic-yard", NounClass.MASCULINE), // POWER
+ new TestCase("fr", "cubic-inch", NounClass.MASCULINE), // POWER
+ new TestCase("fr", "megaliter", NounClass.MASCULINE), // PREFIX
+ new TestCase("fr", "hectoliter", NounClass.MASCULINE), // PREFIX
+ // new TestCase("fr", "pint-metric", NounClass.OTHER), //
+ // new TestCase("fr", "cup-metric", NounClass.OTHER), //
+ new TestCase("fr", "acre-foot", NounClass.FEMININE), // COMPOUND
+ // new TestCase("fr", "bushel", NounClass.OTHER), //
+ // new TestCase("fr", "barrel", NounClass.OTHER), //
+ // Some more French units missing gender:
+ // new TestCase("fr", "degree", NounClass.OTHER), //
+ new TestCase("fr", "square-meter", NounClass.MASCULINE), // COMPOUND
+ // new TestCase("fr", "terabyte", NounClass.OTHER), // PREFIX, byte
+ // new TestCase("fr", "gigabyte", NounClass.OTHER), // PREFIX, byte
+ // new TestCase("fr", "gigabit", NounClass.OTHER), // PREFIX, bit
+ // new TestCase("fr", "megabyte", NounClass.OTHER), // PREFIX, byte
+ // new TestCase("fr", "megabit", NounClass.OTHER), // PREFIX, bit
+ // new TestCase("fr", "kilobyte", NounClass.OTHER), // PREFIX, byte
+ // new TestCase("fr", "kilobit", NounClass.OTHER), // PREFIX, bit
+ // new TestCase("fr", "byte", NounClass.OTHER), //
+ // new TestCase("fr", "bit", NounClass.OTHER), //
+ // new TestCase("fr", "volt", NounClass.OTHER), //
+ new TestCase("fr", "cubic-meter", NounClass.MASCULINE), // POWER
+
+ // gender-lacking builtins within compound units
+ new TestCase("de", "newton-meter-per-second", NounClass.MASCULINE),
+
+ // TODO(ICU-21494): determine whether list genders behave as follows,
+ // and implement proper getListGender support (covering more than just
+ // two genders):
+ // // gender rule for lists of people: de "neutral", fr "maleTaints"
+ // new TestCase("de", "day-and-hour-and-minute", NounClass.NEUTER),
+ // new TestCase("de", "hour-and-minute", NounClass.FEMININE),
+ // new TestCase("fr", "day-and-hour-and-minute", NounClass.MASCULINE),
+ // new TestCase("fr", "hour-and-minute", NounClass.FEMININE),
+ };
+
+ LocalizedNumberFormatter formatter;
+ FormattedNumber fn;
+ for (TestCase t : cases) {
+ formatter = NumberFormatter.with()
+ .unit(MeasureUnit.forIdentifier(t.unitIdentifier))
+ .locale(new ULocale(t.locale));
+ fn = formatter.format(1.1);
+ assertEquals("Testing noun classes with default width, unit: " + t.unitIdentifier +
+ ", locale: " + t.locale,
+ t.expectedNounClass, fn.getNounClass());
+
+ formatter = NumberFormatter.with()
+ .unit(MeasureUnit.forIdentifier(t.unitIdentifier))
+ .unitWidth(UnitWidth.FULL_NAME)
+ .locale(new ULocale(t.locale));
+ fn = formatter.format(1.1);
+ assertEquals("Testing noun classes with UnitWidth.FULL_NAME, unit: " + t.unitIdentifier +
+ ", locale: " + t.locale,
+ t.expectedNounClass, fn.getNounClass());
+ }
+
+ // Make sure getGender does not return garbage for genderless languages
+ formatter = NumberFormatter.with().locale(ULocale.ENGLISH);
+ fn = formatter.format(1.1);
+ assertEquals("getNounClass for not supported language", NounClass.OTHER, fn.getNounClass());
+
+ }
+
@Test
public void unitGender() {
class TestCase {