use_local now uses LanguageIdentifier and proper matching.

Also removed a bunch of deprecated warnings.

Closes #144
This commit is contained in:
Maccesch 2024-08-13 13:45:36 +01:00
parent 1c9bbb6b08
commit d4330c9fb1
12 changed files with 66 additions and 47 deletions

View file

@ -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

View file

@ -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"]

View file

@ -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]

View file

@ -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! {
<p>Locale: <code class="font-bold">{locale}</code></p>
<p>Locale: <code class="font-bold">{move || locale.get().to_string()}</code></p>
}
}

View file

@ -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(

View file

@ -131,9 +131,9 @@ async fn create_media(audio: bool) -> Result<web_sys::MediaStream, JsValue> {
.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)?;

View file

@ -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
}

View file

@ -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(

View file

@ -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<S>(supported: S) -> Signal<String>
pub fn use_locale<S>(supported: S) -> Signal<LanguageIdentifier>
where
S: IntoIterator,
S::Item: Into<String> + Clone + 'static,
S::Item: AsRef<LanguageIdentifier>,
{
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<S>(supported: S, options: UseLocaleOptions) -> Signal<String>
pub fn use_locale_with_options<S>(
supported: S,
options: UseLocaleOptions,
) -> Signal<LanguageIdentifier>
where
S: IntoIterator,
S::Item: Into<String> + Clone + 'static,
S::Item: AsRef<LanguageIdentifier>,
{
let locales = use_locales_with_options(options);
let client_locales = use_locales_with_options(options);
let supported = supported.into_iter().collect::<Vec<_>>();
let supported = supported
.into_iter()
.map(|l| l.as_ref().clone())
.collect::<Vec<_>>();
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);
})
})
}

View file

@ -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);

View file

@ -136,12 +136,12 @@ async fn create_media(video: bool, audio: bool) -> Result<web_sys::MediaStream,
.ok_or_else(|| JsValue::from_str("Failed to access window.navigator"))
.and_then(|n| n.media_devices())?;
let mut constraints = web_sys::MediaStreamConstraints::new();
let constraints = web_sys::MediaStreamConstraints::new();
if video {
constraints.video(&JsValue::from(true));
constraints.set_video(&JsValue::from(true));
}
if audio {
constraints.audio(&JsValue::from(true));
constraints.set_audio(&JsValue::from(true));
}
let promise = media.get_user_media_with_constraints(&constraints)?;

View file

@ -414,39 +414,39 @@ pub struct ShowOptions {
impl ShowOptions {
fn override_notification_options(&self, options: &mut web_sys::NotificationOptions) {
if let Some(direction) = self.direction {
options.dir(direction.into());
options.set_dir(direction.into());
}
if let Some(require_interaction) = self.require_interaction {
options.require_interaction(require_interaction);
options.set_require_interaction(require_interaction);
}
if let Some(body) = &self.body {
options.body(body);
options.set_body(body);
}
if let Some(icon) = &self.icon {
options.icon(icon);
options.set_icon(icon);
}
if let Some(image) = &self.image {
options.image(image);
options.set_image(image);
}
if let Some(language) = &self.language {
options.lang(language);
options.set_lang(language);
}
if let Some(tag) = &self.tag {
options.tag(tag);
options.set_tag(tag);
}
if let Some(renotify) = self.renotify {
options.renotify(renotify);
options.set_renotify(renotify);
}
if let Some(silent) = self.silent {
options.silent(Some(silent));
options.set_silent(Some(silent));
}
}
}