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}