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 _ =
70            crate::use_event_listener(crate::use_window(), leptos::ev::languagechange, move |_| {
71                set_locales.update(|locales| *locales = read_navigator_languages());
72            });
73
74        locales.into()
75    }
76
77    #[cfg(feature = "ssr")]
78    {
79        let UseLocalesOptions {
80            ssr_lang_header_getter,
81        } = options;
82
83        let accept_language = ssr_lang_header_getter().unwrap_or_default();
84
85        let locales = accept_language
86            .split(',')
87            .map(|locale| {
88                locale
89                    .split_once(';')
90                    .map(|x| x.0)
91                    .unwrap_or(locale)
92                    .to_owned()
93            })
94            .collect::<Vec<_>>();
95
96        Signal::derive(move || locales.clone())
97    }
98}
99
100/// Options for [`fn@crate::use_locales_with_options`].
101#[derive(DefaultBuilder)]
102pub struct UseLocalesOptions {
103    /// Getter function to return the string value of the accept languange header.
104    /// When you use one of the features `"axum"` or `"actix"` there's a valid default implementation provided.
105    #[allow(dead_code)]
106    ssr_lang_header_getter: Arc<dyn Fn() -> Option<String>>,
107}
108
109impl Default for UseLocalesOptions {
110    fn default() -> Self {
111        Self {
112            ssr_lang_header_getter: Arc::new(move || {
113                get_header!(ACCEPT_LANGUAGE, use_locales, ssr_lang_header_getter)
114            }),
115        }
116    }
117}