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