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}