Fix typo in 9b54bd30006c008b4a951331b273613d5bac3abf
[pm.git] / intl / unicharutil / util / ICUUtils.cpp
blobe70c845e784caa2f6a750af7b0842aa13ace75d6
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 file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #ifdef MOZILLA_INTERNAL_API
6 #ifdef ENABLE_INTL_API
8 #include "ICUUtils.h"
9 #include "mozilla/Preferences.h"
10 #include "nsIContent.h"
11 #include "nsIDocument.h"
12 #include "nsIToolkitChromeRegistry.h"
13 #include "nsStringGlue.h"
14 #include "unicode/uloc.h"
15 #include "unicode/unum.h"
17 using namespace mozilla;
19 /**
20 * This pref just controls whether we format the number with grouping separator
21 * characters when the internal value is set or updated. It does not stop the
22 * user from typing in a number and using grouping separators.
24 static bool gLocaleNumberGroupingEnabled;
25 static const char LOCALE_NUMBER_GROUPING_PREF_STR[] = "dom.forms.number.grouping";
27 static bool
28 LocaleNumberGroupingIsEnabled()
30 static bool sInitialized = false;
32 if (!sInitialized) {
33 /* check and register ourselves with the pref */
34 Preferences::AddBoolVarCache(&gLocaleNumberGroupingEnabled,
35 LOCALE_NUMBER_GROUPING_PREF_STR,
36 false);
37 sInitialized = true;
40 return gLocaleNumberGroupingEnabled;
43 void
44 ICUUtils::LanguageTagIterForContent::GetNext(nsACString& aBCP47LangTag)
46 if (mCurrentFallbackIndex < 0) {
47 mCurrentFallbackIndex = 0;
48 // Try the language specified by a 'lang'/'xml:lang' attribute on mContent
49 // or any ancestor, if such an attribute is specified:
50 nsAutoString lang;
51 mContent->GetLang(lang);
52 if (!lang.IsEmpty()) {
53 aBCP47LangTag = NS_ConvertUTF16toUTF8(lang);
54 return;
58 if (mCurrentFallbackIndex < 1) {
59 mCurrentFallbackIndex = 1;
60 // Else try the language specified by any Content-Language HTTP header or
61 // pragma directive:
62 nsIDocument* doc = mContent->OwnerDoc();
63 nsAutoString lang;
64 doc->GetContentLanguage(lang);
65 if (!lang.IsEmpty()) {
66 aBCP47LangTag = NS_ConvertUTF16toUTF8(lang);
67 return;
71 if (mCurrentFallbackIndex < 2) {
72 mCurrentFallbackIndex = 2;
73 // Else try the user-agent's locale:
74 nsCOMPtr<nsIToolkitChromeRegistry> cr =
75 mozilla::services::GetToolkitChromeRegistryService();
76 nsAutoCString uaLangTag;
77 if (cr) {
78 cr->GetSelectedLocale(NS_LITERAL_CSTRING("global"), uaLangTag);
80 if (!uaLangTag.IsEmpty()) {
81 aBCP47LangTag = uaLangTag;
82 return;
86 // TODO: Probably not worth it, but maybe have a fourth fallback to using
87 // the OS locale?
89 aBCP47LangTag.Truncate(); // Signal iterator exhausted
92 /* static */ bool
93 ICUUtils::LocalizeNumber(double aValue,
94 LanguageTagIterForContent& aLangTags,
95 nsAString& aLocalizedValue)
97 MOZ_ASSERT(aLangTags.IsAtStart(), "Don't call Next() before passing");
99 static const int32_t kBufferSize = 256;
101 UChar buffer[kBufferSize];
103 nsAutoCString langTag;
104 aLangTags.GetNext(langTag);
105 while (!langTag.IsEmpty()) {
106 UErrorCode status = U_ZERO_ERROR;
107 AutoCloseUNumberFormat format(unum_open(UNUM_DECIMAL, nullptr, 0,
108 langTag.get(), nullptr, &status));
109 unum_setAttribute(format, UNUM_GROUPING_USED,
110 LocaleNumberGroupingIsEnabled());
111 // ICU default is a maximum of 3 significant fractional digits. We don't
112 // want that limit, so we set it to the maximum that a double can represent
113 // (14-16 decimal fractional digits).
114 unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, 16);
115 int32_t length = unum_formatDouble(format, aValue, buffer, kBufferSize,
116 nullptr, &status);
117 NS_ASSERTION(length < kBufferSize &&
118 status != U_BUFFER_OVERFLOW_ERROR &&
119 status != U_STRING_NOT_TERMINATED_WARNING,
120 "Need a bigger buffer?!");
121 if (U_SUCCESS(status)) {
122 ICUUtils::AssignUCharArrayToString(buffer, length, aLocalizedValue);
123 return true;
125 aLangTags.GetNext(langTag);
127 return false;
130 /* static */ double
131 ICUUtils::ParseNumber(nsAString& aValue,
132 LanguageTagIterForContent& aLangTags)
134 MOZ_ASSERT(aLangTags.IsAtStart(), "Don't call Next() before passing");
136 if (aValue.IsEmpty()) {
137 return std::numeric_limits<float>::quiet_NaN();
140 uint32_t length = aValue.Length();
142 nsAutoCString langTag;
143 aLangTags.GetNext(langTag);
144 while (!langTag.IsEmpty()) {
145 UErrorCode status = U_ZERO_ERROR;
146 AutoCloseUNumberFormat format(unum_open(UNUM_DECIMAL, nullptr, 0,
147 langTag.get(), nullptr, &status));
148 int32_t parsePos = 0;
149 static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
150 "Unexpected character size - the following cast is unsafe");
151 double val = unum_parseDouble(format,
152 (const UChar*)PromiseFlatString(aValue).get(),
153 length, &parsePos, &status);
154 if (U_SUCCESS(status) && parsePos == (int32_t)length) {
155 return val;
157 aLangTags.GetNext(langTag);
159 return std::numeric_limits<float>::quiet_NaN();
162 /* static */ void
163 ICUUtils::AssignUCharArrayToString(UChar* aICUString,
164 int32_t aLength,
165 nsAString& aMozString)
167 // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
168 // cast here.
170 static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
171 "Unexpected character size - the following cast is unsafe");
173 aMozString.Assign((const nsAString::char_type*)aICUString, aLength);
175 NS_ASSERTION((int32_t)aMozString.Length() == aLength, "Conversion failed");
178 #if 0
179 /* static */ Locale
180 ICUUtils::BCP47CodeToLocale(const nsAString& aBCP47Code)
182 MOZ_ASSERT(!aBCP47Code.IsEmpty(), "Don't pass an empty BCP 47 code");
184 Locale locale;
185 locale.setToBogus();
187 // BCP47 codes are guaranteed to be ASCII, so lossy conversion is okay
188 NS_LossyConvertUTF16toASCII bcp47code(aBCP47Code);
190 UErrorCode status = U_ZERO_ERROR;
191 int32_t needed;
193 char localeID[256];
194 needed = uloc_forLanguageTag(bcp47code.get(), localeID,
195 PR_ARRAY_SIZE(localeID) - 1, nullptr,
196 &status);
197 MOZ_ASSERT(needed < int32_t(PR_ARRAY_SIZE(localeID)) - 1,
198 "Need a bigger buffer");
199 if (needed <= 0 || U_FAILURE(status)) {
200 return locale;
203 char lang[64];
204 needed = uloc_getLanguage(localeID, lang, PR_ARRAY_SIZE(lang) - 1,
205 &status);
206 MOZ_ASSERT(needed < int32_t(PR_ARRAY_SIZE(lang)) - 1,
207 "Need a bigger buffer");
208 if (needed <= 0 || U_FAILURE(status)) {
209 return locale;
212 char country[64];
213 needed = uloc_getCountry(localeID, country, PR_ARRAY_SIZE(country) - 1,
214 &status);
215 MOZ_ASSERT(needed < int32_t(PR_ARRAY_SIZE(country)) - 1,
216 "Need a bigger buffer");
217 if (needed > 0 && U_SUCCESS(status)) {
218 locale = Locale(lang, country);
221 if (locale.isBogus()) {
222 // Using the country resulted in a bogus Locale, so try with only the lang
223 locale = Locale(lang);
226 return locale;
229 /* static */ void
230 ICUUtils::ToMozString(UnicodeString& aICUString, nsAString& aMozString)
232 // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
233 // cast here.
235 static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
236 "Unexpected character size - the following cast is unsafe");
238 const nsAString::char_type* buf =
239 (const nsAString::char_type*)aICUString.getTerminatedBuffer();
240 aMozString.Assign(buf);
242 NS_ASSERTION(aMozString.Length() == (uint32_t)aICUString.length(),
243 "Conversion failed");
246 /* static */ void
247 ICUUtils::ToICUString(nsAString& aMozString, UnicodeString& aICUString)
249 // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
250 // cast here.
252 static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
253 "Unexpected character size - the following cast is unsafe");
255 aICUString.setTo((UChar*)PromiseFlatString(aMozString).get(),
256 aMozString.Length());
258 NS_ASSERTION(aMozString.Length() == (uint32_t)aICUString.length(),
259 "Conversion failed");
261 #endif
263 #endif /* ENABLE_INTL_API */
264 #endif /* MOZILLA_INTERNAL_API */