Skip to main content

leptos_use/
use_locales.rs

1use crate::utils::get_header;
2use default_struct_builder::DefaultBuilder;
3use leptos::prelude::*;
4use std::sync::Arc;
5
6/// Reactive locales.
7///
8/// If called on the client-side this function returns the value of
9/// [`navigator.languages`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages)
10/// and listens for changes to that property.
11///
12/// See "Server-Side Rendering" below.
13///
14/// ## Demo
15///
16/// [Link to Demo](https://github.com/Synphonyte/leptos-use/tree/main/examples/use_locales)
17///
18/// ## Usage
19///
20/// ```
21/// # use leptos::*;
22/// # use leptos_use::use_locales;
23/// #
24/// # #[component]
25/// # fn Demo() -> impl IntoView {
26/// let locales = use_locales();
27/// #
28/// # view! { }
29/// # }
30/// ```
31///
32/// ## Server-Side Rendering
33///
34/// > Make sure you follow the [instructions in Server-Side Rendering](https://leptos-use.rs/server_side_rendering.html).
35///
36/// On the server this returns the parsed value of the `accept-language` header.
37///
38/// > If you're using `axum` you have to enable the `"axum"` feature in your Cargo.toml.
39/// > In case it's `actix-web` enable the feature `"actix"`.
40///
41/// ### Bring your own header
42///
43/// In case you're neither using Axum nor Actix or the default implementation is not to your liking,
44/// you can provide your own way of reading the language header value using the option
45/// [`crate::UseLocalesOptions::ssr_lang_header_getter`].
46pub fn use_locales() -> Signal<Vec<String>> {
47    use_locales_with_options(UseLocalesOptions::default())
48}
49
50/// Version of [`fn@crate::use_locales`] that takes a `UseLocalesOptions`. See [`fn@crate::use_locales`] for how to use.
51pub fn use_locales_with_options(options: UseLocalesOptions) -> Signal<Vec<String>> {
52    #[cfg(not(feature = "ssr"))]
53    {
54        let _ = options;
55
56        let read_navigator_languages = || {
57            let window = crate::use_window();
58            let navigator = window.navigator();
59            navigator
60                .map(|navigator| navigator.languages().to_vec())
61                .unwrap_or_default()
62                .into_iter()
63                .filter_map(|x| x.as_string())
64                .collect::<Vec<String>>()
65        };
66
67        let (locales, set_locales) = signal(read_navigator_languages());
68
69        let _ = crate::use_event_listener_with_options(
70            crate::use_window(),
71            leptos::ev::languagechange,
72            move |_| {
73                set_locales.update(|locales| *locales = read_navigator_languages());
74            },
75            crate::UseEventListenerOptions::default().passive(true),
76        );
77
78        locales.into()
79    }
80
81    #[cfg(feature = "ssr")]
82    {
83        let UseLocalesOptions {
84            ssr_lang_header_getter,
85        } = options;
86
87        let accept_language = ssr_lang_header_getter().unwrap_or_default();
88
89        let locales = accept_language
90            .split(',')
91            .map(|locale| {
92                locale
93                    .split_once(';')
94                    .map(|x| x.0)
95                    .unwrap_or(locale)
96                    .to_owned()
97            })
98            .collect::<Vec<_>>();
99
100        Signal::derive(move || locales.clone())
101    }
102}
103
104/// Options for [`fn@crate::use_locales_with_options`].
105#[derive(DefaultBuilder)]
106pub struct UseLocalesOptions {
107    /// Getter function to return the string value of the accept languange header.
108    /// When you use one of the features `"axum"` or `"actix"` there's a valid default implementation provided.
109    #[allow(dead_code)]
110    ssr_lang_header_getter: Arc<dyn Fn() -> Option<String>>,
111}
112
113impl Default for UseLocalesOptions {
114    fn default() -> Self {
115        Self {
116            ssr_lang_header_getter: Arc::new(move || {
117                get_header!(ACCEPT_LANGUAGE, use_locales, ssr_lang_header_getter)
118            }),
119        }
120    }
121}