diff --git a/CHANGELOG.md b/CHANGELOG.md index 9871816..9d114a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated to web_sys 0.3.70 which unfortunately is breaking some things. - `use_clipboard` doesn't need the unstable flags anymore. +- `use_local` now uses `unic_langid::LanguageIdentifier` and proper locale matching (thanks to @mondeja). ## [0.11.4] - 2024-08-12 diff --git a/Cargo.toml b/Cargo.toml index 6a18c69..cf8fd6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,8 @@ leptos-spin = { version = "0.1", optional = true } num = { version = "0.4", optional = true } paste = "1" thiserror = "1" -wasm-bindgen = "0.2.92" +unic-langid = "0.9" +wasm-bindgen = ">=0.2.93" wasm-bindgen-futures = "0.4" [dependencies.web-sys] @@ -129,11 +130,12 @@ features = [ ] [dev-dependencies] +codee = { version = "0.1", features = ["json_serde", "msgpack_serde", "base64", "prost"] } getrandom = { version = "0.2", features = ["js"] } leptos_meta = "0.6" rand = "0.8" -codee = { version = "0.1", features = ["json_serde", "msgpack_serde", "base64", "prost"] } serde = { version = "1", features = ["derive"] } +unic-langid = { version = "0.9", features = ["macros"] } [features] actix = ["dep:actix-web", "dep:leptos_actix", "dep:http0_2"] diff --git a/examples/use_locale/Cargo.toml b/examples/use_locale/Cargo.toml index eb1cdfd..1a2555a 100644 --- a/examples/use_locale/Cargo.toml +++ b/examples/use_locale/Cargo.toml @@ -9,6 +9,7 @@ console_error_panic_hook = "0.1" console_log = "1" log = "0.4" leptos-use = { path = "../..", features = ["docs"] } +unic-langid = { version = "0.9", features = ["macros"] } web-sys = "0.3" [dev-dependencies] diff --git a/examples/use_locale/src/main.rs b/examples/use_locale/src/main.rs index 36e132c..a0fe2a6 100644 --- a/examples/use_locale/src/main.rs +++ b/examples/use_locale/src/main.rs @@ -1,13 +1,14 @@ use leptos::*; use leptos_use::docs::demo_or_body; use leptos_use::use_locale; +use unic_langid::langid_slice; #[component] fn Demo() -> impl IntoView { - let locale = use_locale(["en", "de", "fr"]); + let locale = use_locale(langid_slice!["en", "de", "fr"]); view! { -

Locale: {locale}

+

Locale: {move || locale.get().to_string()}

} } diff --git a/src/storage/use_storage.rs b/src/storage/use_storage.rs index 0d8f903..91b56c0 100644 --- a/src/storage/use_storage.rs +++ b/src/storage/use_storage.rs @@ -215,8 +215,8 @@ where queue_microtask(move || { // TODO : better to use a BroadcastChannel (use_broadcast_channel)? // Note: we cannot construct a full StorageEvent so we _must_ rely on a custom event - let mut custom = web_sys::CustomEventInit::new(); - custom.detail(&JsValue::from_str(&key)); + let custom = web_sys::CustomEventInit::new(); + custom.set_detail(&JsValue::from_str(&key)); let result = window() .dispatch_event( &web_sys::CustomEvent::new_with_event_init_dict( diff --git a/src/use_display_media.rs b/src/use_display_media.rs index 8174fba..a89d9c2 100644 --- a/src/use_display_media.rs +++ b/src/use_display_media.rs @@ -131,9 +131,9 @@ async fn create_media(audio: bool) -> Result { .ok_or_else(|| JsValue::from_str("Failed to access window.navigator")) .and_then(|n| n.media_devices())?; - let mut constraints = web_sys::DisplayMediaStreamConstraints::new(); + let constraints = web_sys::DisplayMediaStreamConstraints::new(); if audio { - constraints.audio(&JsValue::from(true)); + constraints.set_audio(&JsValue::from(true)); } let promise = media.get_display_media_with_constraints(&constraints)?; diff --git a/src/use_geolocation.rs b/src/use_geolocation.rs index 2aeda83..7494ef3 100644 --- a/src/use_geolocation.rs +++ b/src/use_geolocation.rs @@ -186,10 +186,10 @@ impl UseGeolocationOptions { .. } = self; - let mut options = web_sys::PositionOptions::new(); - options.enable_high_accuracy(*enable_high_accuracy); - options.maximum_age(*maximum_age); - options.timeout(*timeout); + let options = web_sys::PositionOptions::new(); + options.set_enable_high_accuracy(*enable_high_accuracy); + options.set_maximum_age(*maximum_age); + options.set_timeout(*timeout); options } diff --git a/src/use_intersection_observer.rs b/src/use_intersection_observer.rs index cca3343..f17f1f3 100644 --- a/src/use_intersection_observer.rs +++ b/src/use_intersection_observer.rs @@ -154,8 +154,9 @@ where return; } - let mut options = web_sys::IntersectionObserverInit::new(); - options.root_margin(&root_margin).threshold( + let options = web_sys::IntersectionObserverInit::new(); + options.set_root_margin(&root_margin); + options.set_threshold( &thresholds .iter() .copied() @@ -165,7 +166,7 @@ where if let Some(Some(root)) = root { let root: web_sys::Element = root.clone().into(); - options.root(Some(&root)); + options.set_root(Some(&root)); } let obs = web_sys::IntersectionObserver::new_with_options( diff --git a/src/use_locale.rs b/src/use_locale.rs index 7b31825..be781a4 100644 --- a/src/use_locale.rs +++ b/src/use_locale.rs @@ -1,14 +1,16 @@ use crate::{use_locales_with_options, UseLocalesOptions}; use leptos::*; +use unic_langid::LanguageIdentifier; /// Reactive locale matching. /// /// Returns the first matching locale given by [`fn@crate::use_locales`] that is also found in /// the `supported` list. In case there is no match, then the first locale in `supported` will be -/// returned. If `supported` is empty, the empty string is returned. +/// returned. /// -/// Matching is done by checking if an accepted locale from `use_locales` starts with a supported -/// locale. If a match is found the locale from the `supported` list is returned. +/// > If `supported` is empty, this function will panic! +/// +/// Matching is done by using the [`fn@unic_langid::LanguageIdentifier::matches`] method. /// /// ## Demo /// @@ -19,10 +21,11 @@ use leptos::*; /// ``` /// # use leptos::*; /// # use leptos_use::use_locale; +/// use unic_langid::langid_slice; /// # /// # #[component] /// # fn Demo() -> impl IntoView { -/// let locale = use_locale(["en", "de", "fr"]); +/// let locale = use_locale(langid_slice!["en", "de", "fr"]); /// # /// # view! { } /// # } @@ -31,45 +34,55 @@ use leptos::*; /// ## Server-Side Rendering /// /// See [`fn@crate::use_locales`] -pub fn use_locale(supported: S) -> Signal +pub fn use_locale(supported: S) -> Signal where S: IntoIterator, - S::Item: Into + Clone + 'static, + S::Item: AsRef, { use_locale_with_options(supported, UseLocaleOptions::default()) } /// Version of [`fn@crate::use_locale`] that takes a `UseLocaleOptions`. See [`fn@crate::use_locale`] for how to use. -pub fn use_locale_with_options(supported: S, options: UseLocaleOptions) -> Signal +pub fn use_locale_with_options( + supported: S, + options: UseLocaleOptions, +) -> Signal where S: IntoIterator, - S::Item: Into + Clone + 'static, + S::Item: AsRef, { - let locales = use_locales_with_options(options); + let client_locales = use_locales_with_options(options); - let supported = supported.into_iter().collect::>(); + let supported = supported + .into_iter() + .map(|l| l.as_ref().clone()) + .collect::>(); + + const EMPTY_ERR_MSG: &'static str = "Empty supported list. You have to provide at least one locale in the `supported` parameter"; + assert!(supported.len() > 0, "{}", EMPTY_ERR_MSG); Signal::derive(move || { let supported = supported.clone(); - locales.with(|locales| { + client_locales.with(|clienht_locales| { let mut first_supported = None; for s in supported { - let s = s.into(); - if first_supported.is_none() { first_supported = Some(s.clone()); } - for locale in locales { - if locale.starts_with(&s) { + for client_locale in clienht_locales { + let client_locale: LanguageIdentifier = client_locale + .parse() + .expect("Client should provide a list of valid unicode locales"); + if client_locale.matches(&s, true, true) { return s; } } } - first_supported.unwrap_or_else(|| "".to_string()) + unreachable!("{}", EMPTY_ERR_MSG); }) }) } diff --git a/src/use_scroll.rs b/src/use_scroll.rs index 9db4ab1..8c0fc80 100644 --- a/src/use_scroll.rs +++ b/src/use_scroll.rs @@ -231,14 +231,14 @@ where if let Some(element) = element { let element = element.into(); - let mut scroll_options = web_sys::ScrollToOptions::new(); - scroll_options.behavior(behavior.get_untracked().into()); + let scroll_options = web_sys::ScrollToOptions::new(); + scroll_options.set_behavior(behavior.get_untracked().into()); if let Some(x) = x { - scroll_options.left(x); + scroll_options.set_left(x); } if let Some(y) = y { - scroll_options.top(y); + scroll_options.set_top(y); } element.scroll_to_with_scroll_to_options(&scroll_options); diff --git a/src/use_user_media.rs b/src/use_user_media.rs index 2336f38..76b1eed 100644 --- a/src/use_user_media.rs +++ b/src/use_user_media.rs @@ -136,12 +136,12 @@ async fn create_media(video: bool, audio: bool) -> Result