123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
- #ifdef MOZILLA_INTERNAL_API
- #include "ICUUtils.h"
- #include "mozilla/Preferences.h"
- #include "nsIContent.h"
- #include "nsIDocument.h"
- #include "nsIToolkitChromeRegistry.h"
- #include "nsStringGlue.h"
- #include "unicode/uloc.h"
- #include "unicode/unum.h"
- using namespace mozilla;
- /**
- * This pref just controls whether we format the number with grouping separator
- * characters when the internal value is set or updated. It does not stop the
- * user from typing in a number and using grouping separators.
- */
- static bool gLocaleNumberGroupingEnabled;
- static const char LOCALE_NUMBER_GROUPING_PREF_STR[] = "dom.forms.number.grouping";
- static bool
- LocaleNumberGroupingIsEnabled()
- {
- static bool sInitialized = false;
- if (!sInitialized) {
- /* check and register ourselves with the pref */
- Preferences::AddBoolVarCache(&gLocaleNumberGroupingEnabled,
- LOCALE_NUMBER_GROUPING_PREF_STR,
- false);
- sInitialized = true;
- }
- return gLocaleNumberGroupingEnabled;
- }
- void
- ICUUtils::LanguageTagIterForContent::GetNext(nsACString& aBCP47LangTag)
- {
- if (mCurrentFallbackIndex < 0) {
- mCurrentFallbackIndex = 0;
- // Try the language specified by a 'lang'/'xml:lang' attribute on mContent
- // or any ancestor, if such an attribute is specified:
- nsAutoString lang;
- mContent->GetLang(lang);
- if (!lang.IsEmpty()) {
- aBCP47LangTag = NS_ConvertUTF16toUTF8(lang);
- return;
- }
- }
- if (mCurrentFallbackIndex < 1) {
- mCurrentFallbackIndex = 1;
- // Else try the language specified by any Content-Language HTTP header or
- // pragma directive:
- nsIDocument* doc = mContent->OwnerDoc();
- nsAutoString lang;
- doc->GetContentLanguage(lang);
- if (!lang.IsEmpty()) {
- aBCP47LangTag = NS_ConvertUTF16toUTF8(lang);
- return;
- }
- }
- if (mCurrentFallbackIndex < 2) {
- mCurrentFallbackIndex = 2;
- // Else try the user-agent's locale:
- nsCOMPtr<nsIToolkitChromeRegistry> cr =
- mozilla::services::GetToolkitChromeRegistryService();
- nsAutoCString uaLangTag;
- if (cr) {
- cr->GetSelectedLocale(NS_LITERAL_CSTRING("global"), true, uaLangTag);
- }
- if (!uaLangTag.IsEmpty()) {
- aBCP47LangTag = uaLangTag;
- return;
- }
- }
- // TODO: Probably not worth it, but maybe have a fourth fallback to using
- // the OS locale?
- aBCP47LangTag.Truncate(); // Signal iterator exhausted
- }
- /* static */ bool
- ICUUtils::LocalizeNumber(double aValue,
- LanguageTagIterForContent& aLangTags,
- nsAString& aLocalizedValue)
- {
- MOZ_ASSERT(aLangTags.IsAtStart(), "Don't call Next() before passing");
- static const int32_t kBufferSize = 256;
- UChar buffer[kBufferSize];
- nsAutoCString langTag;
- aLangTags.GetNext(langTag);
- while (!langTag.IsEmpty()) {
- UErrorCode status = U_ZERO_ERROR;
- AutoCloseUNumberFormat format(unum_open(UNUM_DECIMAL, nullptr, 0,
- langTag.get(), nullptr, &status));
- unum_setAttribute(format, UNUM_GROUPING_USED,
- LocaleNumberGroupingIsEnabled());
- // ICU default is a maximum of 3 significant fractional digits. We don't
- // want that limit, so we set it to the maximum that a double can represent
- // (14-16 decimal fractional digits).
- unum_setAttribute(format, UNUM_MAX_FRACTION_DIGITS, 16);
- int32_t length = unum_formatDouble(format, aValue, buffer, kBufferSize,
- nullptr, &status);
- NS_ASSERTION(length < kBufferSize &&
- status != U_BUFFER_OVERFLOW_ERROR &&
- status != U_STRING_NOT_TERMINATED_WARNING,
- "Need a bigger buffer?!");
- if (U_SUCCESS(status)) {
- ICUUtils::AssignUCharArrayToString(buffer, length, aLocalizedValue);
- return true;
- }
- aLangTags.GetNext(langTag);
- }
- return false;
- }
- /* static */ double
- ICUUtils::ParseNumber(nsAString& aValue,
- LanguageTagIterForContent& aLangTags)
- {
- MOZ_ASSERT(aLangTags.IsAtStart(), "Don't call Next() before passing");
- if (aValue.IsEmpty()) {
- return std::numeric_limits<float>::quiet_NaN();
- }
- uint32_t length = aValue.Length();
- nsAutoCString langTag;
- aLangTags.GetNext(langTag);
- while (!langTag.IsEmpty()) {
- UErrorCode status = U_ZERO_ERROR;
- AutoCloseUNumberFormat format(unum_open(UNUM_DECIMAL, nullptr, 0,
- langTag.get(), nullptr, &status));
- int32_t parsePos = 0;
- static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
- "Unexpected character size - the following cast is unsafe");
- double val = unum_parseDouble(format,
- (const UChar*)PromiseFlatString(aValue).get(),
- length, &parsePos, &status);
- if (U_SUCCESS(status) && parsePos == (int32_t)length) {
- return val;
- }
- aLangTags.GetNext(langTag);
- }
- return std::numeric_limits<float>::quiet_NaN();
- }
- /* static */ void
- ICUUtils::AssignUCharArrayToString(UChar* aICUString,
- int32_t aLength,
- nsAString& aMozString)
- {
- // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
- // cast here.
- static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
- "Unexpected character size - the following cast is unsafe");
- aMozString.Assign((const nsAString::char_type*)aICUString, aLength);
- NS_ASSERTION((int32_t)aMozString.Length() == aLength, "Conversion failed");
- }
- /* static */ nsresult
- ICUUtils::UErrorToNsResult(const UErrorCode aErrorCode)
- {
- if (U_SUCCESS(aErrorCode)) {
- return NS_OK;
- }
- switch(aErrorCode) {
- case U_ILLEGAL_ARGUMENT_ERROR:
- return NS_ERROR_INVALID_ARG;
- case U_MEMORY_ALLOCATION_ERROR:
- return NS_ERROR_OUT_OF_MEMORY;
- default:
- return NS_ERROR_FAILURE;
- }
- }
- #if 0
- /* static */ Locale
- ICUUtils::BCP47CodeToLocale(const nsAString& aBCP47Code)
- {
- MOZ_ASSERT(!aBCP47Code.IsEmpty(), "Don't pass an empty BCP 47 code");
- Locale locale;
- locale.setToBogus();
- // BCP47 codes are guaranteed to be ASCII, so lossy conversion is okay
- NS_LossyConvertUTF16toASCII bcp47code(aBCP47Code);
- UErrorCode status = U_ZERO_ERROR;
- int32_t needed;
- char localeID[256];
- needed = uloc_forLanguageTag(bcp47code.get(), localeID,
- PR_ARRAY_SIZE(localeID) - 1, nullptr,
- &status);
- MOZ_ASSERT(needed < int32_t(PR_ARRAY_SIZE(localeID)) - 1,
- "Need a bigger buffer");
- if (needed <= 0 || U_FAILURE(status)) {
- return locale;
- }
- char lang[64];
- needed = uloc_getLanguage(localeID, lang, PR_ARRAY_SIZE(lang) - 1,
- &status);
- MOZ_ASSERT(needed < int32_t(PR_ARRAY_SIZE(lang)) - 1,
- "Need a bigger buffer");
- if (needed <= 0 || U_FAILURE(status)) {
- return locale;
- }
- char country[64];
- needed = uloc_getCountry(localeID, country, PR_ARRAY_SIZE(country) - 1,
- &status);
- MOZ_ASSERT(needed < int32_t(PR_ARRAY_SIZE(country)) - 1,
- "Need a bigger buffer");
- if (needed > 0 && U_SUCCESS(status)) {
- locale = Locale(lang, country);
- }
- if (locale.isBogus()) {
- // Using the country resulted in a bogus Locale, so try with only the lang
- locale = Locale(lang);
- }
- return locale;
- }
- /* static */ void
- ICUUtils::ToMozString(UnicodeString& aICUString, nsAString& aMozString)
- {
- // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
- // cast here.
- static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
- "Unexpected character size - the following cast is unsafe");
- const nsAString::char_type* buf =
- (const nsAString::char_type*)aICUString.getTerminatedBuffer();
- aMozString.Assign(buf);
- NS_ASSERTION(aMozString.Length() == (uint32_t)aICUString.length(),
- "Conversion failed");
- }
- /* static */ void
- ICUUtils::ToICUString(nsAString& aMozString, UnicodeString& aICUString)
- {
- // Both ICU's UnicodeString and Mozilla's nsAString use UTF-16, so we can
- // cast here.
- static_assert(sizeof(UChar) == 2 && sizeof(nsAString::char_type) == 2,
- "Unexpected character size - the following cast is unsafe");
- aICUString.setTo((UChar*)PromiseFlatString(aMozString).get(),
- aMozString.Length());
- NS_ASSERTION(aMozString.Length() == (uint32_t)aICUString.length(),
- "Conversion failed");
- }
- #endif
- #endif /* MOZILLA_INTERNAL_API */
|