mirror of https://github.com/boa-dev/boa.git
Browse Source
<!--- Thank you for contributing to Boa! Please fill out the template below, and remove or add any information as you feel neccesary. ---> This pull request is related to #1180. It changes the following: - Creates the `Intl` global - Adds the `Intl.getCanonicalLocales` method At the moment it does not actually use ICU4X behind the scenes; `Intl.getCanonicalLocales` simply acts as if all the locales passed are canonical locales. This will not be the case in the final PR. Co-authored-by: RageKnify <RageKnify@gmail.com>pull/1686/head
hle0
3 years ago
2 changed files with 144 additions and 0 deletions
@ -0,0 +1,141 @@ |
|||||||
|
//! This module implements the global `Intl` object.
|
||||||
|
//!
|
||||||
|
//! `Intl` is a built-in object that has properties and methods for i18n. It's not a function object.
|
||||||
|
//!
|
||||||
|
//! More information:
|
||||||
|
//! - [ECMAScript reference][spec]
|
||||||
|
//!
|
||||||
|
//! [spec]: https://tc39.es/ecma402/#intl-object
|
||||||
|
|
||||||
|
use indexmap::IndexSet; |
||||||
|
|
||||||
|
use crate::{ |
||||||
|
builtins::{Array, BuiltIn, JsArgs}, |
||||||
|
object::ObjectInitializer, |
||||||
|
property::Attribute, |
||||||
|
symbol::WellKnownSymbols, |
||||||
|
BoaProfiler, Context, JsResult, JsString, JsValue, |
||||||
|
}; |
||||||
|
|
||||||
|
/// JavaScript `Intl` object.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] |
||||||
|
pub(crate) struct Intl; |
||||||
|
|
||||||
|
impl BuiltIn for Intl { |
||||||
|
const NAME: &'static str = "Intl"; |
||||||
|
|
||||||
|
const ATTRIBUTE: Attribute = Attribute::WRITABLE |
||||||
|
.union(Attribute::NON_ENUMERABLE) |
||||||
|
.union(Attribute::CONFIGURABLE); |
||||||
|
|
||||||
|
fn init(context: &mut Context) -> JsValue { |
||||||
|
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); |
||||||
|
|
||||||
|
let string_tag = WellKnownSymbols::to_string_tag(); |
||||||
|
let object = ObjectInitializer::new(context) |
||||||
|
.function(Self::get_canonical_locales, "getCanonicalLocales", 1) |
||||||
|
.property( |
||||||
|
string_tag, |
||||||
|
Self::NAME, |
||||||
|
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, |
||||||
|
) |
||||||
|
.build(); |
||||||
|
|
||||||
|
object.into() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Intl { |
||||||
|
fn canonicalize_locale(locale: &str) -> JsString { |
||||||
|
JsString::new(locale) |
||||||
|
} |
||||||
|
|
||||||
|
fn canonicalize_locale_list( |
||||||
|
args: &[JsValue], |
||||||
|
context: &mut Context, |
||||||
|
) -> JsResult<Vec<JsString>> { |
||||||
|
// https://tc39.es/ecma402/#sec-canonicalizelocalelist
|
||||||
|
// 1. If locales is undefined, then
|
||||||
|
let locales = args.get_or_undefined(0); |
||||||
|
if locales.is_undefined() { |
||||||
|
// a. Return a new empty List.
|
||||||
|
return Ok(Vec::new()); |
||||||
|
} |
||||||
|
|
||||||
|
let locales = &args[0]; |
||||||
|
|
||||||
|
// 2. Let seen be a new empty List.
|
||||||
|
let mut seen = IndexSet::new(); |
||||||
|
|
||||||
|
// 3. If Type(locales) is String or Type(locales) is Object and locales has an [[InitializedLocale]] internal slot, then
|
||||||
|
// TODO: check if Type(locales) is object and handle the internal slots
|
||||||
|
let o = if locales.is_string() { |
||||||
|
// a. Let O be CreateArrayFromList(« locales »).
|
||||||
|
Array::create_array_from_list([locales.clone()], context) |
||||||
|
} else { |
||||||
|
// 4. Else,
|
||||||
|
// a. Let O be ? ToObject(locales).
|
||||||
|
locales.to_object(context)? |
||||||
|
}; |
||||||
|
|
||||||
|
// 5. Let len be ? ToLength(? Get(O, "length")).
|
||||||
|
let len = o.length_of_array_like(context)?; |
||||||
|
|
||||||
|
// 6 Let k be 0.
|
||||||
|
// 7. Repeat, while k < len,
|
||||||
|
for k in 0..len { |
||||||
|
// a. Let Pk be ToString(k).
|
||||||
|
// b. Let kPresent be ? HasProperty(O, Pk).
|
||||||
|
let k_present = o.has_property(k, context)?; |
||||||
|
// c. If kPresent is true, then
|
||||||
|
if k_present { |
||||||
|
// i. Let kValue be ? Get(O, Pk).
|
||||||
|
let k_value = o.get(k, context)?; |
||||||
|
// ii. If Type(kValue) is not String or Object, throw a TypeError exception.
|
||||||
|
if !(k_value.is_object() || k_value.is_string()) { |
||||||
|
return Err(context |
||||||
|
.throw_type_error("locale should be a String or Object") |
||||||
|
.unwrap_err()); |
||||||
|
} |
||||||
|
// iii. If Type(kValue) is Object and kValue has an [[InitializedLocale]] internal slot, then
|
||||||
|
// TODO: handle checks for InitializedLocale internal slot (there should be an if statement here)
|
||||||
|
// 1. Let tag be kValue.[[Locale]].
|
||||||
|
// iv. Else,
|
||||||
|
// 1. Let tag be ? ToString(kValue).
|
||||||
|
let tag = k_value.to_string(context)?; |
||||||
|
// v. If IsStructurallyValidLanguageTag(tag) is false, throw a RangeError exception.
|
||||||
|
// TODO: implement `IsStructurallyValidLanguageTag`
|
||||||
|
|
||||||
|
// vi. Let canonicalizedTag be CanonicalizeUnicodeLocaleId(tag).
|
||||||
|
seen.insert(Self::canonicalize_locale(&tag)); |
||||||
|
// vii. If canonicalizedTag is not an element of seen, append canonicalizedTag as the last element of seen.
|
||||||
|
} |
||||||
|
// d. Increase k by 1.
|
||||||
|
} |
||||||
|
|
||||||
|
// 8. Return seen.
|
||||||
|
Ok(seen.into_iter().collect::<Vec<JsString>>()) |
||||||
|
} |
||||||
|
|
||||||
|
/// Returns an array containing the canonical locale names.
|
||||||
|
///
|
||||||
|
/// More information:
|
||||||
|
/// - [ECMAScript reference][spec]
|
||||||
|
/// - [MDN docs][mdn]
|
||||||
|
///
|
||||||
|
/// [spec]: https://tc39.es/ecma402/#sec-intl.getcanonicallocales
|
||||||
|
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/getCanonicalLocales
|
||||||
|
pub(crate) fn get_canonical_locales( |
||||||
|
_: &JsValue, |
||||||
|
args: &[JsValue], |
||||||
|
context: &mut Context, |
||||||
|
) -> JsResult<JsValue> { |
||||||
|
// 1. Let ll be ? CanonicalizeLocaleList(locales).
|
||||||
|
let ll = Self::canonicalize_locale_list(args, context)?; |
||||||
|
// 2. Return CreateArrayFromList(ll).
|
||||||
|
Ok(JsValue::Object(Array::create_array_from_list( |
||||||
|
ll.into_iter().map(|x| x.into()), |
||||||
|
context, |
||||||
|
))) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue