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/// On the server this returns the parsed value of the `accept-language` header.
35///
36/// > If you're using `axum` you have to enable the `"axum"` feature in your Cargo.toml.
37/// > In case it's `actix-web` enable the feature `"actix"`.
38///
39/// ### Bring your own header
40///
41/// In case you're neither using Axum nor Actix or the default implementation is not to your liking,
42/// you can provide your own way of reading the language header value using the option
43/// [`crate::UseLocalesOptions::ssr_lang_header_getter`].
44pub fn use_locales() -> Signal<Vec<String>> {
45    use_locales_with_options(UseLocalesOptions::default())
46}
47
48/// Version of [`fn@crate::use_locales`] that takes a `UseLocalesOptions`. See [`fn@crate::use_locales`] for how to use.
49pub fn use_locales_with_options(options: UseLocalesOptions) -> Signal<Vec<String>> {
50    #[cfg(not(feature = "ssr"))]
51    {
52        let _ = options;
53
54        let read_navigator_languages = || {
55            let window = crate::use_window();
56            let navigator = window.navigator();
57            navigator
58                .map(|navigator| navigator.languages().to_vec())
59                .unwrap_or_default()
60                .into_iter()
61                .filter_map(|x| x.as_string())
62                .collect::<Vec<String>>()
63        };
64
65        let (locales, set_locales) = signal(read_navigator_languages());
66
67        let _ =
68            crate::use_event_listener(crate::use_window(), leptos::ev::languagechange, move |_| {
69                set_locales.update(|locales| *locales = read_navigator_languages());
70            });
71
72        locales.into()
73    }
74
75    #[cfg(feature = "ssr")]
76    {
77        let UseLocalesOptions {
78            ssr_lang_header_getter,
79        } = options;
80
81        let accept_language = ssr_lang_header_getter().unwrap_or_default();
82
83        let locales = accept_language
84            .split(',')
85            .map(|locale| {
86                locale
87                    .split_once(';')
88                    .map(|x| x.0)
89                    .unwrap_or(locale)
90                    .to_owned()
91            })
92            .collect::<Vec<_>>();
93
94        Signal::derive(move || locales.clone())
95    }
96}
97
98/// Options for [`fn@crate::use_locales_with_options`].
99#[derive(DefaultBuilder)]
100pub struct UseLocalesOptions {
101    /// Getter function to return the string value of the accept languange header.
102    /// When you use one of the features `"axum"` or `"actix"` there's a valid default implementation provided.
103    #[allow(dead_code)]
104    ssr_lang_header_getter: Arc<dyn Fn() -> Option<String>>,
105}
106
107impl Default for UseLocalesOptions {
108    fn default() -> Self {
109        Self {
110            ssr_lang_header_getter: Arc::new(move || {
111                get_header!(ACCEPT_LANGUAGE, use_locales, ssr_lang_header_getter)
112            }),
113        }
114    }
115}