Skip to content

Commit daf86d3

Browse files
authored
typedarray: use locale-sensitive separator in %TypedArray%.prototype.toLocaleString (#5089)
## Summary Update `%TypedArray%.prototype.toLocaleString` to use a locale-sensitive separator when the `intl` feature is enabled, aligning its behavior with `Array.prototype.toLocaleString`. ### ref - #5081 Previously, the TypedArray implementation always used the hardcoded separator `", "`. When the `intl` feature is enabled, `Array.prototype.toLocaleString` already derives the separator using ICU's `ListFormatter`. This PR updates the TypedArray implementation to use the same locale-aware separator logic. When the `intl` feature is disabled, the implementation correctly falls back to `", "`. ## Changes - Use ICU `ListFormatter` via the `intl_provider` to compute a locale-sensitive separator when the `intl` feature is enabled. - Fall back to the default separator `", "` when `intl` is disabled. - Hoist extraction of `locales` and `options` arguments outside the iteration loop to avoid repeated lookups. - Remove the unused `utf16` import from the module. ## Specification This aligns the implementation with: - **ECMAScript §23.2.3.31** `%TypedArray%.prototype.toLocaleString` which specifies that the algorithm should behave the same as: - **ECMAScript §23.1.3.32** `Array.prototype.toLocaleString` except that `TypedArrayLength` is used instead of `LengthOfArrayLike`. ## Verification The following checks were performed: - `cargo fmt` - `cargo clippy` - `cargo test` - Test262 tests for TypedArray locale string conversion: <img width="760" height="184" alt="Screenshot 2026-03-15 at 8 27 06 AM" src="https://github.com/user-attachments/assets/e75a497e-3fab-44ce-85ae-3c3c33e01149" /> All existing tests pass and no regressions were introduced. ## Notes The behavior now matches the locale-sensitive list separator behavior used by `Array.prototype.toLocaleString` when `intl` support is enabled. --------- Signed-off-by: mrhapile <allinonegaming3456@gmail.com>
1 parent e4bbd39 commit daf86d3

File tree

2 files changed

+31
-12
lines changed

2 files changed

+31
-12
lines changed

core/engine/src/builtins/intl/number_format/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -761,7 +761,10 @@ fn unwrap_number_format(nf: &JsValue, context: &mut Context) -> JsResult<JsObjec
761761
/// Abstract operation [`ToIntlMathematicalValue ( value )`][spec].
762762
///
763763
/// [spec]: https://tc39.es/ecma402/#sec-tointlmathematicalvalue
764-
fn to_intl_mathematical_value(value: &JsValue, context: &mut Context) -> JsResult<Decimal> {
764+
pub(crate) fn to_intl_mathematical_value(
765+
value: &JsValue,
766+
context: &mut Context,
767+
) -> JsResult<Decimal> {
765768
// 1. Let primValue be ? ToPrimitive(value, number).
766769
let prim_value = value.to_primitive(context, PreferredType::Number)?;
767770

core/engine/src/builtins/typed_array/builtin.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::{
33
sync::atomic::Ordering,
44
};
55

6-
use boa_macros::utf16;
76
use num_traits::Zero;
87

98
use super::{
@@ -1211,7 +1210,7 @@ impl BuiltinTypedArray {
12111210
};
12121211

12131212
// 6. Let R be the empty String.
1214-
let mut r = Vec::new();
1213+
let mut r = Vec::with_capacity(len as usize);
12151214

12161215
// 7. Let k be 0.
12171216
// 8. Repeat, while k < len,
@@ -2503,24 +2502,44 @@ impl BuiltinTypedArray {
25032502
(o.array_length(buf_len), is_fixed_len)
25042503
};
25052504

2505+
let locales = args.get_or_undefined(0);
2506+
let options = args.get_or_undefined(1);
2507+
25062508
let separator = {
25072509
#[cfg(feature = "intl")]
25082510
{
2509-
// TODO: this should eventually return a locale-sensitive separator.
2510-
utf16!(", ")
2511+
use crate::builtins::intl::locale::default_locale;
2512+
use icu_list::{
2513+
ListFormatter, ListFormatterPreferences, options::ListFormatterOptions,
2514+
};
2515+
2516+
let locale = default_locale(context.intl_provider().locale_canonicalizer()?);
2517+
let preferences = ListFormatterPreferences::from(&locale);
2518+
let formatter = ListFormatter::try_new_unit_with_buffer_provider(
2519+
context.intl_provider().erased_provider(),
2520+
preferences,
2521+
ListFormatterOptions::default(),
2522+
)
2523+
.map_err(|e| JsNativeError::typ().with_message(e.to_string()))?;
2524+
2525+
// Ask ICU for the list pattern literal by formatting two empty elements.
2526+
// For many locales this yields ", ", but it may differ.
2527+
js_string!(
2528+
formatter.format_to_string(std::iter::once("").chain(std::iter::once("")))
2529+
)
25112530
}
25122531

25132532
#[cfg(not(feature = "intl"))]
25142533
{
2515-
utf16!(", ")
2534+
js_string!(", ")
25162535
}
25172536
};
25182537

2519-
let mut r = Vec::new();
2538+
let mut r = Vec::with_capacity((len + len.saturating_sub(1)) as usize);
25202539

25212540
for k in 0..len {
25222541
if k > 0 {
2523-
r.extend_from_slice(separator);
2542+
r.extend(separator.iter());
25242543
}
25252544

25262545
let next_element = array.get(k, context)?;
@@ -2531,10 +2550,7 @@ impl BuiltinTypedArray {
25312550
let s = next_element
25322551
.invoke(
25332552
js_string!("toLocaleString"),
2534-
&[
2535-
args.get_or_undefined(0).clone(),
2536-
args.get_or_undefined(1).clone(),
2537-
],
2553+
&[locales.clone(), options.clone()],
25382554
context,
25392555
)?
25402556
.to_string(context)?;

0 commit comments

Comments
 (0)