PluralRules.cpp (5201B)
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 #include "mozilla/intl/PluralRules.h" 6 7 #include "mozilla/intl/ICU4CGlue.h" 8 #include "mozilla/intl/NumberFormat.h" 9 #include "mozilla/intl/NumberRangeFormat.h" 10 #include "mozilla/Span.h" 11 #include "ScopedICUObject.h" 12 13 #include "unicode/unum.h" 14 #include "unicode/upluralrules.h" 15 #include "unicode/ustring.h" 16 17 namespace mozilla::intl { 18 19 PluralRules::PluralRules(UPluralRules*& aPluralRules, 20 UniquePtr<NumberFormat>&& aNumberFormat, 21 UniquePtr<NumberRangeFormat>&& aNumberRangeFormat) 22 : mPluralRules(aPluralRules), 23 mNumberFormat(std::move(aNumberFormat)), 24 mNumberRangeFormat(std::move(aNumberRangeFormat)) { 25 MOZ_ASSERT(aPluralRules); 26 aPluralRules = nullptr; 27 } 28 29 Result<UniquePtr<PluralRules>, ICUError> PluralRules::TryCreate( 30 const std::string_view aLocale, const PluralRulesOptions& aOptions) { 31 auto numberFormat = 32 NumberFormat::TryCreate(aLocale, aOptions.ToNumberFormatOptions()); 33 34 if (numberFormat.isErr()) { 35 return Err(numberFormat.unwrapErr()); 36 } 37 38 auto numberRangeFormat = NumberRangeFormat::TryCreate( 39 aLocale, aOptions.ToNumberRangeFormatOptions()); 40 41 if (numberRangeFormat.isErr()) { 42 return Err(numberRangeFormat.unwrapErr()); 43 } 44 45 UErrorCode status = U_ZERO_ERROR; 46 auto pluralType = aOptions.mPluralType == PluralRules::Type::Cardinal 47 ? UPLURAL_TYPE_CARDINAL 48 : UPLURAL_TYPE_ORDINAL; 49 UPluralRules* pluralRules = uplrules_openForType( 50 AssertNullTerminatedString(aLocale), pluralType, &status); 51 52 if (U_FAILURE(status)) { 53 return Err(ToICUError(status)); 54 } 55 56 return UniquePtr<PluralRules>(new PluralRules( 57 pluralRules, numberFormat.unwrap(), numberRangeFormat.unwrap())); 58 } 59 60 Result<PluralRules::Keyword, ICUError> PluralRules::Select( 61 const double aNumber) const { 62 char16_t keyword[MAX_KEYWORD_LENGTH]; 63 64 auto lengthResult = mNumberFormat->selectFormatted( 65 aNumber, keyword, MAX_KEYWORD_LENGTH, mPluralRules); 66 67 if (lengthResult.isErr()) { 68 return Err(lengthResult.unwrapErr()); 69 } 70 71 return KeywordFromUtf16(Span(keyword, lengthResult.unwrap())); 72 } 73 74 Result<PluralRules::Keyword, ICUError> PluralRules::SelectRange( 75 double aStart, double aEnd) const { 76 char16_t keyword[MAX_KEYWORD_LENGTH]; 77 78 auto lengthResult = mNumberRangeFormat->selectForRange( 79 aStart, aEnd, keyword, MAX_KEYWORD_LENGTH, mPluralRules); 80 81 if (lengthResult.isErr()) { 82 return Err(lengthResult.unwrapErr()); 83 } 84 85 return KeywordFromUtf16(Span(keyword, lengthResult.unwrap())); 86 } 87 88 Result<EnumSet<PluralRules::Keyword>, ICUError> PluralRules::Categories() 89 const { 90 UErrorCode status = U_ZERO_ERROR; 91 UEnumeration* enumeration = uplrules_getKeywords(mPluralRules, &status); 92 if (U_FAILURE(status)) { 93 return Err(ToICUError(status)); 94 } 95 96 ScopedICUObject<UEnumeration, uenum_close> closeEnum(enumeration); 97 EnumSet<PluralRules::Keyword> set; 98 99 while (true) { 100 int32_t keywordLength; 101 const char* keyword = uenum_next(enumeration, &keywordLength, &status); 102 if (U_FAILURE(status)) { 103 return Err(ToICUError(status)); 104 } 105 106 if (!keyword) { 107 break; 108 } 109 110 set += KeywordFromAscii(Span(keyword, keywordLength)); 111 } 112 113 return set; 114 } 115 116 PluralRules::Keyword PluralRules::KeywordFromUtf16( 117 Span<const char16_t> aKeyword) { 118 static constexpr auto kZero = MakeStringSpan(u"zero"); 119 static constexpr auto kOne = MakeStringSpan(u"one"); 120 static constexpr auto kTwo = MakeStringSpan(u"two"); 121 static constexpr auto kFew = MakeStringSpan(u"few"); 122 static constexpr auto kMany = MakeStringSpan(u"many"); 123 124 if (aKeyword == kZero) { 125 return PluralRules::Keyword::Zero; 126 } 127 if (aKeyword == kOne) { 128 return PluralRules::Keyword::One; 129 } 130 if (aKeyword == kTwo) { 131 return PluralRules::Keyword::Two; 132 } 133 if (aKeyword == kFew) { 134 return PluralRules::Keyword::Few; 135 } 136 if (aKeyword == kMany) { 137 return PluralRules::Keyword::Many; 138 } 139 140 MOZ_ASSERT(aKeyword == MakeStringSpan(u"other")); 141 return PluralRules::Keyword::Other; 142 } 143 144 PluralRules::Keyword PluralRules::KeywordFromAscii(Span<const char> aKeyword) { 145 static constexpr auto kZero = MakeStringSpan("zero"); 146 static constexpr auto kOne = MakeStringSpan("one"); 147 static constexpr auto kTwo = MakeStringSpan("two"); 148 static constexpr auto kFew = MakeStringSpan("few"); 149 static constexpr auto kMany = MakeStringSpan("many"); 150 151 if (aKeyword == kZero) { 152 return PluralRules::Keyword::Zero; 153 } 154 if (aKeyword == kOne) { 155 return PluralRules::Keyword::One; 156 } 157 if (aKeyword == kTwo) { 158 return PluralRules::Keyword::Two; 159 } 160 if (aKeyword == kFew) { 161 return PluralRules::Keyword::Few; 162 } 163 if (aKeyword == kMany) { 164 return PluralRules::Keyword::Many; 165 } 166 167 MOZ_ASSERT(aKeyword == MakeStringSpan("other")); 168 return PluralRules::Keyword::Other; 169 } 170 171 PluralRules::~PluralRules() { 172 if (mPluralRules) { 173 uplrules_close(mPluralRules); 174 mPluralRules = nullptr; 175 } 176 } 177 178 } // namespace mozilla::intl