Skip to content

Commit

Permalink
Merge pull request #189 from Baptistemontan/resolve_locale_fn
Browse files Browse the repository at this point in the history
Function to resolve the locale without a context
  • Loading branch information
Baptistemontan authored Feb 12, 2025
2 parents 1cb028e + e905c87 commit aab926a
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 13 deletions.
1 change: 1 addition & 0 deletions docs/book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- [`t_format!`](./usage/09_t_format.md)
- [`t_plural!`](./usage/10_t_plural.md)
- [Constant Access](./usage/11_const_access.md)
- [Server functions](./usage/12_serverfn.md)
- [More Informations](./infos/README.md)
- [Locale Resolution](./infos/01_locale_resol.md)
- [Reduce Binary Size](./reduce_size/README.md)
Expand Down
12 changes: 12 additions & 0 deletions docs/book/src/usage/12_serverfn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Server functions

There is no context in server functions, so you can't call `use_i18n`. You could provide a context if you want,
and it would work as expected, but if you just want to access what locale the user is using you can use the `resolve_locale` function:

```rust
#[server]
async fn get_locale() -> Result<Locale, ServerFnError> {
let locale: Locale = leptos_i18n::locale::resolve_locale();
Ok(locale)
}
```
11 changes: 6 additions & 5 deletions leptos_i18n/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ pub type CookieOptions<L> = UseCookieOptions<
<FromToStringCodec as codee::Decoder<L>>::Error,
>;

const ENABLE_COOKIE: bool = cfg!(feature = "cookie");
pub(crate) const ENABLE_COOKIE: bool = cfg!(feature = "cookie");

const COOKIE_PREFERED_LANG: &str = "i18n_pref_locale";

Expand Down Expand Up @@ -167,14 +167,14 @@ where
L: Locale,
{
/// Should set a cookie to keep track of the locale when page reload (default to true) (do nothing without the "cookie" feature)
enable_cookie: bool,
pub enable_cookie: bool,
/// Give a custom name to the cookie (default to the crate default value) (do nothing without the "cookie" feature or if `enable_cookie` is false)
#[builder(into)]
cookie_name: Cow<'a, str>,
pub cookie_name: Cow<'a, str>,
/// Options for the cookie, the value is of type `leptos_use::UseCookieOptions<Locale>` (default to `Default::default`)
cookie_options: CookieOptions<L>,
pub cookie_options: CookieOptions<L>,
/// Options to pass to `leptos_use::use_locales`.
ssr_lang_header_getter: UseLocalesOptions,
pub ssr_lang_header_getter: UseLocalesOptions,
}

impl<L: Locale> Default for I18nContextOptions<'_, L> {
Expand Down Expand Up @@ -209,6 +209,7 @@ pub fn init_i18n_context_with_options<L: Locale>(options: I18nContextOptions<L>)

init_context_inner::<L>(set_lang_cookie, initial_locale)
}

/// Initialize a `I18nContext` without providing it.
#[track_caller]
pub fn init_i18n_context<L: Locale>() -> I18nContext<L> {
Expand Down
24 changes: 20 additions & 4 deletions leptos_i18n/src/fetch_locale.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ pub fn fetch_locale<L: Locale>(current_cookie: Option<L>, options: UseLocalesOpt
}
}

pub fn get_accepted_locale<L: Locale>(options: UseLocalesOptions) -> L {
leptos_use::use_locales_with_options(options)
.with_untracked(|accepted| L::find_locale(accepted))
}

pub fn resolve_locale<L: Locale>(current_cookie: Option<L>, options: UseLocalesOptions) -> L {
cfg!(feature = "hydrate")
.then(get_locale_from_html)
.flatten()
.or(current_cookie)
.unwrap_or_else(move || get_accepted_locale(options))
}

pub fn signal_once_then<T: Clone + PartialEq + Send + Sync + 'static>(
start: T,
then: Memo<T>,
Expand Down Expand Up @@ -46,9 +59,8 @@ fn fetch_locale_ssr<L: Locale>(current_cookie: Option<L>, accepted_locale: Memo<
signal_maybe_once_then(current_cookie, accepted_locale)
}

// hydrate fetch
fn fetch_locale_hydrate<L: Locale>(current_cookie: Option<L>, accepted_locale: Memo<L>) -> Memo<L> {
let base_locale = leptos::prelude::document()
fn get_locale_from_html<L: Locale>() -> Option<L> {
leptos::prelude::document()
.document_element()
.and_then(|el| match el.get_attribute("lang") {
None => {
Expand All @@ -58,7 +70,11 @@ fn fetch_locale_hydrate<L: Locale>(current_cookie: Option<L>, accepted_locale: M
Some(lang) => Some(lang),
})
.and_then(|lang| L::from_str(&lang).ok())
.or(current_cookie);
}

// hydrate fetch
fn fetch_locale_hydrate<L: Locale>(current_cookie: Option<L>, accepted_locale: Memo<L>) -> Memo<L> {
let base_locale = get_locale_from_html().or(current_cookie);

signal_maybe_once_then(base_locale, accepted_locale)
}
Expand Down
6 changes: 2 additions & 4 deletions leptos_i18n/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,17 +125,16 @@
//! ```
pub mod context;
pub mod display;
mod fetch_locale;
mod fetch_translations;
mod langid;
pub mod locale;
mod locale_traits;
mod macro_helpers;
mod macros;
// mod routing;
mod scopes;

pub mod display;

pub use macro_helpers::formatting;

pub use locale_traits::{Direction, Locale, LocaleKeys};
Expand All @@ -162,7 +161,6 @@ pub mod __private {
pub mod fetch_translations {
pub use crate::fetch_translations::*;
}
// pub use crate::fetch_locale::get_locale_from_path_inner;
#[cfg(feature = "plurals")]
pub use crate::formatting::get_plural_rules;
pub use crate::macro_helpers::*;
Expand Down
44 changes: 44 additions & 0 deletions leptos_i18n/src/locale.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//! Contain utilities for locales
use codee::string::FromToStringCodec;
use leptos::prelude::*;

use crate::{
context::{I18nContextOptions, ENABLE_COOKIE},
fetch_locale, Locale,
};

/// Same as `resolve_locale` but with some cookies options.
pub fn resolve_locale_with_options<L: Locale>(options: I18nContextOptions<L>) -> L {
let I18nContextOptions {
enable_cookie,
cookie_name,
cookie_options,
ssr_lang_header_getter,
} = options;
let (lang_cookie, _) = if ENABLE_COOKIE && enable_cookie {
leptos_use::use_cookie_with_options::<L, FromToStringCodec>(&cookie_name, cookie_options)
} else {
let (lang_cookie, set_lang_cookie) = signal(None);
(lang_cookie.into(), set_lang_cookie)
};
fetch_locale::resolve_locale(lang_cookie.get_untracked(), ssr_lang_header_getter)
}

/// Resolve the locale.
///
/// This as the same behavior as calling `init_i18n_context().get_locale_untracked()`.
///
/// This function primary usage is to access a user locale in a server function, but is not constrained to it.
///
/// Here is the list of detection methods, sorted in priorities:
/// 1. The "lang" attribute is set on the `<html>` element in hydrate
/// 1. A cookie is present that contains a previously detected locale
/// 1. A locale can be matched based on the [`Accept-Language` header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language) in SSR
/// 1. A locale can be matched based on the [`navigator.languages` API](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages) in CSR
/// 1. As a last resort, the default locale is used.
///
/// *note*: this function does not take into account URL locale prefix when using `I18nRoute` (e.g. `/en/about`)
pub fn resolve_locale<L: Locale>() -> L {
resolve_locale_with_options(Default::default())
}

0 comments on commit aab926a

Please sign in to comment.