diff --git a/intl/l10n/FluentBundle.cpp b/intl/l10n/FluentBundle.cpp index b641a7af4948..2a2fa71b8515 100644 --- a/intl/l10n/FluentBundle.cpp +++ b/intl/l10n/FluentBundle.cpp @@ -7,10 +7,10 @@ #include "FluentBundle.h" #include "nsContentUtils.h" #include "mozilla/dom/UnionTypes.h" +#include "mozilla/intl/NumberFormat.h" #include "nsIInputStream.h" #include "nsStringFwd.h" #include "nsTArray.h" -#include "unicode/numberformatter.h" #include "unicode/datefmt.h" using namespace mozilla::dom; @@ -209,62 +209,95 @@ void FluentBundle::FormatPattern(JSContext* aCx, const FluentPattern& aPattern, extern "C" { ffi::RawNumberFormatter* FluentBuiltInNumberFormatterCreate( const nsCString* aLocale, const ffi::FluentNumberOptionsRaw* aOptions) { - auto grouping = aOptions->use_grouping - ? UNumberGroupingStrategy::UNUM_GROUPING_AUTO - : UNumberGroupingStrategy::UNUM_GROUPING_OFF; - - auto formatter = - icu::number::NumberFormatter::with().grouping(grouping).integerWidth( - icu::number::IntegerWidth::zeroFillTo( - aOptions->minimum_integer_digits)); - - // JS uses ROUND_HALFUP strategy, ICU by default uses ROUND_HALFEVEN. - // See: http://userguide.icu-project.org/formatparse/numbers/rounding-modes - formatter = formatter.roundingMode(UNUM_ROUND_HALFUP); - - if (aOptions->style == ffi::FluentNumberStyleRaw::Currency) { - UErrorCode ec = U_ZERO_ERROR; - formatter = formatter.unit(icu::CurrencyUnit(aOptions->currency.get(), ec)); - MOZ_ASSERT(U_SUCCESS(ec), "Failed to format the currency unit."); - } - if (aOptions->style == ffi::FluentNumberStyleRaw::Percent) { - formatter = formatter.unit(icu::NoUnit::percent()) - .scale(icu::number::Scale::powerOfTen(2)); + NumberFormatOptions options; + switch (aOptions->style) { + case ffi::FluentNumberStyleRaw::Decimal: + break; + case ffi::FluentNumberStyleRaw::Currency: { + std::string currency = aOptions->currency.get(); + switch (aOptions->currency_display) { + case ffi::FluentNumberCurrencyDisplayStyleRaw::Symbol: + options.mCurrency = Some(std::make_pair( + currency, NumberFormatOptions::CurrencyDisplayStyle::Symbol)); + break; + case ffi::FluentNumberCurrencyDisplayStyleRaw::Code: + options.mCurrency = Some(std::make_pair( + currency, NumberFormatOptions::CurrencyDisplayStyle::Code)); + break; + case ffi::FluentNumberCurrencyDisplayStyleRaw::Name: + options.mCurrency = Some(std::make_pair( + currency, NumberFormatOptions::CurrencyDisplayStyle::Name)); + break; + default: + MOZ_ASSERT_UNREACHABLE(); + break; + } + } break; + case ffi::FluentNumberStyleRaw::Percent: + options.mPercent = true; + break; + default: + MOZ_ASSERT_UNREACHABLE(); + break; } + options.mUseGrouping = aOptions->use_grouping; + options.mMinIntegerDigits = Some(aOptions->minimum_integer_digits); + if (aOptions->minimum_significant_digits >= 0 || aOptions->maximum_significant_digits >= 0) { - auto precision = icu::number::Precision::minMaxSignificantDigits( - aOptions->minimum_significant_digits, - aOptions->maximum_significant_digits) - .minMaxFraction(aOptions->minimum_fraction_digits, - aOptions->maximum_fraction_digits); - formatter = formatter.precision(precision); + options.mSignificantDigits = + Some(std::make_pair(aOptions->minimum_significant_digits, + aOptions->maximum_significant_digits)); } else { - auto precision = icu::number::Precision::minMaxFraction( - aOptions->minimum_fraction_digits, aOptions->maximum_fraction_digits); - formatter = formatter.precision(precision); + options.mFractionDigits = Some(std::make_pair( + aOptions->minimum_fraction_digits, aOptions->maximum_fraction_digits)); } return reinterpret_cast( - formatter.locale(aLocale->get()).clone().orphan()); + new NumberFormat(aLocale->get(), options)); } uint8_t* FluentBuiltInNumberFormatterFormat( - const ffi::RawNumberFormatter* aFormatter, double input, - uint32_t* aOutCount) { - auto formatter = - reinterpret_cast( - aFormatter); - UErrorCode ec = U_ZERO_ERROR; - icu::number::FormattedNumber result = formatter->formatDouble(input, ec); - icu::UnicodeString str = result.toTempString(ec); - return reinterpret_cast(ToNewUTF8String( - nsDependentSubstring(str.getBuffer(), str.length()), aOutCount)); + const ffi::RawNumberFormatter* aFormatter, double input, size_t* aOutCount, + size_t* aOutCapacity) { + const NumberFormat* nf = reinterpret_cast(aFormatter); + + class Buffer { + public: + using CharType = uint8_t; + + bool allocate(size_t size) { + mBuffer.reset(reinterpret_cast(malloc(size))); + mCapacity = size; + return true; + } + + void* data() { return mBuffer.get(); } + + size_t size() const { return mCapacity; } + + void written(size_t amount) { mWritten = amount; } + + size_t mWritten = 0; + size_t mCapacity = 0; + CharType* mData = nullptr; + + struct FreePolicy { + void operator()(const void* ptr) { free(const_cast(ptr)); } + }; + + UniquePtr mBuffer; + } buffer; + + nf->format(input, buffer); + *aOutCount = buffer.mWritten; + *aOutCapacity = buffer.mCapacity; + return buffer.mBuffer.release(); } void FluentBuiltInNumberFormatterDestroy(ffi::RawNumberFormatter* aFormatter) { - delete reinterpret_cast(aFormatter); + delete reinterpret_cast(aFormatter); } /* DateTime */ diff --git a/intl/l10n/moz.build b/intl/l10n/moz.build index 71cfcc7173b9..e51c5a2c07f5 100644 --- a/intl/l10n/moz.build +++ b/intl/l10n/moz.build @@ -41,6 +41,8 @@ LOCAL_INCLUDES += [ "/dom/base", ] +USE_LIBS += ["intlcomponents"] + if CONFIG["COMPILE_ENVIRONMENT"]: CbindgenHeader("fluent_ffi_generated.h", inputs=["/intl/l10n/rust/fluent-ffi"]) diff --git a/intl/l10n/rust/fluent-ffi/src/builtins.rs b/intl/l10n/rust/fluent-ffi/src/builtins.rs index 399f89ec2af6..13897c29464c 100644 --- a/intl/l10n/rust/fluent-ffi/src/builtins.rs +++ b/intl/l10n/rust/fluent-ffi/src/builtins.rs @@ -39,12 +39,17 @@ impl NumberFormat { pub fn format(&self, input: f64) -> String { unsafe { let mut byte_count = 0; - let buffer = - ffi::FluentBuiltInNumberFormatterFormat(self.raw.as_ptr(), input, &mut byte_count); + let mut capacity = 0; + let buffer = ffi::FluentBuiltInNumberFormatterFormat( + self.raw.as_ptr(), + input, + &mut byte_count, + &mut capacity, + ); if buffer.is_null() { return String::new(); } - String::from_raw_parts(buffer, byte_count as usize, byte_count as usize) + String::from_raw_parts(buffer, byte_count, capacity) } } } diff --git a/intl/l10n/rust/fluent-ffi/src/ffi.rs b/intl/l10n/rust/fluent-ffi/src/ffi.rs index 94d43d5ce035..4818c9a31850 100644 --- a/intl/l10n/rust/fluent-ffi/src/ffi.rs +++ b/intl/l10n/rust/fluent-ffi/src/ffi.rs @@ -136,7 +136,8 @@ extern "C" { pub fn FluentBuiltInNumberFormatterFormat( formatter: *const RawNumberFormatter, input: f64, - out_count: &mut u32, + out_count: &mut usize, + out_capacity: &mut usize, ) -> *mut u8; pub fn FluentBuiltInNumberFormatterDestroy(formatter: *mut RawNumberFormatter);