PluralRules.h (6868B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #ifndef intl_components_PluralRules_h_ 6 #define intl_components_PluralRules_h_ 7 8 #include <string_view> 9 #include <utility> 10 11 #include "mozilla/intl/ICUError.h" 12 #include "mozilla/intl/NumberFormat.h" 13 #include "mozilla/intl/NumberRangeFormat.h" 14 #include "mozilla/EnumSet.h" 15 #include "mozilla/Maybe.h" 16 #include "mozilla/Result.h" 17 #include "mozilla/Span.h" 18 19 #include "unicode/utypes.h" 20 21 namespace mozilla::intl { 22 23 class PluralRules final { 24 public: 25 /** 26 * The set of keywords that a PluralRules object uses. 27 * 28 * https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.resolvedoptions 29 */ 30 enum class Keyword : uint8_t { 31 Few, 32 Many, 33 One, 34 Other, 35 Two, 36 Zero, 37 }; 38 39 /** 40 * The two different types of PluralRules objects that can be created. 41 * 42 * https://tc39.es/ecma402/#sec-properties-of-intl-pluralrules-instances 43 */ 44 enum class Type : uint8_t { 45 Cardinal, 46 Ordinal, 47 }; 48 49 PluralRules(const PluralRules&) = delete; 50 PluralRules& operator=(const PluralRules&) = delete; 51 52 /** 53 * Attempts to construct a PluralRules with the given locale and options. 54 */ 55 // TODO(1709880) use mozilla::Span instead of std::string_view. 56 static Result<UniquePtr<PluralRules>, ICUError> TryCreate( 57 std::string_view aLocale, const PluralRulesOptions& aOptions); 58 59 /** 60 * Returns the PluralRules keyword that corresponds to the |aNumber|. 61 * 62 * https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.select 63 */ 64 Result<PluralRules::Keyword, ICUError> Select(double aNumber) const; 65 66 /** 67 * Returns the PluralRules keyword that corresponds to the range from |aStart| 68 * to |aEnd|. 69 * 70 * https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.selectrange 71 */ 72 Result<PluralRules::Keyword, ICUError> SelectRange(double aStart, 73 double aEnd) const; 74 75 /** 76 * Returns an EnumSet with the plural-rules categories that are supported by 77 * the locale that the PluralRules instance was created with. 78 */ 79 Result<EnumSet<PluralRules::Keyword>, ICUError> Categories() const; 80 81 ~PluralRules(); 82 83 private: 84 // The longest keyword is "other" 85 static const size_t MAX_KEYWORD_LENGTH = 5; 86 87 UPluralRules* mPluralRules = nullptr; 88 UniquePtr<NumberFormat> mNumberFormat; 89 UniquePtr<NumberRangeFormat> mNumberRangeFormat; 90 91 PluralRules(UPluralRules*&, UniquePtr<NumberFormat>&&, 92 UniquePtr<NumberRangeFormat>&&); 93 94 /** 95 * Returns the PluralRules::Keyword that matches the UTF-16 string. 96 * Strings must be [u"few", u"many", u"one", u"other", u"two", u"zero"] 97 */ 98 static PluralRules::Keyword KeywordFromUtf16(Span<const char16_t> aKeyword); 99 100 /** 101 * Returns the PluralRules::Keyword that matches the ASCII string. 102 * Strings must be ["few", "many", "one", "other", "two", "zero"] 103 */ 104 static PluralRules::Keyword KeywordFromAscii(Span<const char> aKeyword); 105 }; 106 107 /** 108 * Options required for constructing a PluralRules object. 109 */ 110 struct MOZ_STACK_CLASS PluralRulesOptions { 111 /** 112 * Creates a NumberFormatOptions from the PluralRulesOptions. 113 */ 114 NumberFormatOptions ToNumberFormatOptions() const { 115 NumberFormatOptions options; 116 options.mRoundingMode = NumberFormatOptions::RoundingMode::HalfExpand; 117 118 if (mFractionDigits.isSome()) { 119 options.mFractionDigits.emplace(mFractionDigits.ref()); 120 } 121 122 if (mMinIntegerDigits.isSome()) { 123 options.mMinIntegerDigits.emplace(mMinIntegerDigits.ref()); 124 } 125 126 if (mSignificantDigits.isSome()) { 127 options.mSignificantDigits.emplace(mSignificantDigits.ref()); 128 } 129 130 options.mStripTrailingZero = mStripTrailingZero; 131 132 options.mRoundingIncrement = mRoundingIncrement; 133 134 options.mRoundingMode = NumberFormatOptions::RoundingMode(mRoundingMode); 135 136 options.mRoundingPriority = 137 NumberFormatOptions::RoundingPriority(mRoundingPriority); 138 139 return options; 140 } 141 /** 142 * Creates a NumberFormatOptions from the PluralRulesOptions. 143 */ 144 NumberRangeFormatOptions ToNumberRangeFormatOptions() const { 145 NumberRangeFormatOptions options; 146 options.mRoundingMode = NumberRangeFormatOptions::RoundingMode::HalfExpand; 147 options.mRangeCollapse = NumberRangeFormatOptions::RangeCollapse::None; 148 options.mRangeIdentityFallback = 149 NumberRangeFormatOptions::RangeIdentityFallback::Range; 150 151 if (mFractionDigits.isSome()) { 152 options.mFractionDigits.emplace(mFractionDigits.ref()); 153 } 154 155 if (mMinIntegerDigits.isSome()) { 156 options.mMinIntegerDigits.emplace(mMinIntegerDigits.ref()); 157 } 158 159 if (mSignificantDigits.isSome()) { 160 options.mSignificantDigits.emplace(mSignificantDigits.ref()); 161 } 162 163 options.mStripTrailingZero = mStripTrailingZero; 164 165 options.mRoundingIncrement = mRoundingIncrement; 166 167 options.mRoundingMode = NumberFormatOptions::RoundingMode(mRoundingMode); 168 169 options.mRoundingPriority = 170 NumberFormatOptions::RoundingPriority(mRoundingPriority); 171 172 return options; 173 } 174 175 /** 176 * Set the plural type between cardinal and ordinal. 177 * 178 * https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.resolvedoptions 179 */ 180 PluralRules::Type mPluralType = PluralRules::Type::Cardinal; 181 182 /** 183 * Set the minimum number of integer digits. |min| must be a non-zero 184 * number. 185 * 186 * https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.resolvedoptions 187 */ 188 Maybe<uint32_t> mMinIntegerDigits; 189 190 /** 191 * Set the fraction digits settings. |min| can be zero, |max| must be 192 * larger-or-equal to |min|. 193 * 194 * https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.resolvedoptions 195 */ 196 Maybe<std::pair<uint32_t, uint32_t>> mFractionDigits; 197 198 /** 199 * Set the significant digits settings. |min| must be a non-zero number, |max| 200 * must be larger-or-equal to |min|. 201 * 202 * https://tc39.es/ecma402/#sec-intl.pluralrules.prototype.resolvedoptions 203 */ 204 Maybe<std::pair<uint32_t, uint32_t>> mSignificantDigits; 205 206 /** 207 * Set to true to strip trailing zeros after the decimal point for integer 208 * values. 209 */ 210 bool mStripTrailingZero = false; 211 212 /** 213 * Set the rounding increment, which must be a non-zero number. 214 */ 215 uint32_t mRoundingIncrement = 1; 216 217 /** 218 * Set the rounding mode. 219 */ 220 using RoundingMode = NumberFormatOptions::RoundingMode; 221 RoundingMode mRoundingMode = RoundingMode::HalfExpand; 222 223 /** 224 * Set the rounding priority. |mFractionDigits| and |mSignificantDigits| must 225 * both be set if the rounding priority isn't equal to "auto". 226 */ 227 using RoundingPriority = NumberFormatOptions::RoundingPriority; 228 RoundingPriority mRoundingPriority = RoundingPriority::Auto; 229 }; 230 231 } // namespace mozilla::intl 232 233 #endif