From 0006e0e3733612289add35f9f3d559b3c5762013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Mond=C3=A9jar=20Rubio?= Date: Sun, 16 Jun 2024 16:14:19 +0200 Subject: [PATCH] Add `cookie_attrs` parameter to `leptos_fluent!` macro (#123) --- CHANGELOG.md | 9 +++++-- Cargo.lock | 4 +-- README.md | 10 +++++--- end2end/tests/TODO.md | 1 + end2end/tests/cookie.rs | 4 +-- examples/csr-complete/src/lib.rs | 4 +++ examples/ssr-hydrate-actix/src/app.rs | 1 + examples/ssr-hydrate-axum/src/app.rs | 1 + leptos-fluent-macros/Cargo.toml | 2 +- leptos-fluent-macros/src/lib.rs | 15 +++++++++++ leptos-fluent-macros/src/loader.rs | 15 +++++++++++ leptos-fluent/Cargo.toml | 2 +- leptos-fluent/README.md | 10 +++++--- leptos-fluent/src/cookie.rs | 37 +++++++++++++++------------ leptos-fluent/src/lib.rs | 10 +++++--- 15 files changed, 89 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fc8eb39..7ee29bef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # CHANGELOG -## Unreleased - [0.0.37] +## 2024-06-16 - [0.0.37] ### Bug fixes @@ -11,6 +11,11 @@ - Notify invalid Fluent message identifiers checking translations. +### New features + +- Add `cookie_attrs` parameter to `leptos_fluent!` macro to set cookie + attributes. + ## 2024-06-15 - [0.0.36] ### Bug fixes @@ -209,7 +214,7 @@ - Added all ISO-639-1 and ISO-639-2 languages. -[0.0.37]: https://github.com/mondeja/leptos-fluent/compare/v0.0.36...master +[0.0.37]: https://github.com/mondeja/leptos-fluent/compare/v0.0.36...v0.0.37 [0.0.36]: https://github.com/mondeja/leptos-fluent/compare/v0.0.35...v0.0.36 [0.0.35]: https://github.com/mondeja/leptos-fluent/compare/v0.0.34...v0.0.35 [0.0.34]: https://github.com/mondeja/leptos-fluent/compare/v0.0.33...v0.0.34 diff --git a/Cargo.lock b/Cargo.lock index 6271bc58..4a5184f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1446,7 +1446,7 @@ dependencies = [ [[package]] name = "leptos-fluent" -version = "0.0.36" +version = "0.0.37" dependencies = [ "fluent-templates", "leptos", @@ -1478,7 +1478,7 @@ dependencies = [ [[package]] name = "leptos-fluent-macros" -version = "0.0.36" +version = "0.0.37" dependencies = [ "fluent-syntax", "fluent-templates", diff --git a/README.md b/README.md index c5e62815..4c939409 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Add the following to your `Cargo.toml` file: ```toml [dependencies] -leptos-fluent = "0.0.36" +leptos-fluent = "0.0.37" fluent-templates = "0.9" [features] @@ -117,6 +117,11 @@ fn App() -> impl IntoView { // Get the initial language from `navigator.languages` if not // found in the local storage. By default, it is `false`. initial_language_from_navigator: true, + // Attributes to set for the language cookie. By default is `""`. + cookie_attrs: "SameSite=Strict; Secure; path=/; max-age=600", + // Update the language on cookie when using the method `I18n.set_language`. + // By default, it is `false`. + set_language_to_cookie: true, // Server side options // ------------------- @@ -131,9 +136,6 @@ fn App() -> impl IntoView { cookie_name: "lang", // Get the initial language from cookie. By default, it is `false`. initial_language_from_cookie: true, - // Update the language on cookie when using the method `I18n.set_language`. - // By default, it is `false`. - set_language_to_cookie: true, // URL parameter name to use discovering the initial language // of the user. By default is `"lang"`. url_param: "lang", diff --git a/end2end/tests/TODO.md b/end2end/tests/TODO.md index 3fb1a759..a167dc0c 100644 --- a/end2end/tests/TODO.md +++ b/end2end/tests/TODO.md @@ -5,3 +5,4 @@ - `initial_language_from_navigator` - `initial_language_from_accept_language_header` - `set_language_to_cookie` +- `cookie_attrs` diff --git a/end2end/tests/cookie.rs b/end2end/tests/cookie.rs index 6db72b60..c89f0550 100644 --- a/end2end/tests/cookie.rs +++ b/end2end/tests/cookie.rs @@ -33,13 +33,13 @@ async fn test_cookie() { assert_eq!(element_text("p"), "Select a language:"); unmount!(); - cookie::set(COOKIE_NAME, "es"); + cookie::set(COOKIE_NAME, "es", ""); mount!(App); assert!(es().checked()); assert_eq!(element_text("p"), "Selecciona un idioma:"); unmount!(); - cookie::set(COOKIE_NAME, "en"); + cookie::set(COOKIE_NAME, "en", ""); mount!(App); assert!(en().checked()); assert_eq!(element_text("p"), "Select a language:"); diff --git a/examples/csr-complete/src/lib.rs b/examples/csr-complete/src/lib.rs index dd88a7cd..a0532015 100644 --- a/examples/csr-complete/src/lib.rs +++ b/examples/csr-complete/src/lib.rs @@ -17,6 +17,10 @@ pub fn App() -> impl IntoView { locales: "./locales", check_translations: "./src/**/*.rs", sync_html_tag_lang: true, + cookie_name: "lang", + cookie_attrs: "SameSite=Strict; Secure; path=/; max-age=600", + set_language_to_cookie: true, + initial_language_from_cookie: true, url_param: "lang", initial_language_from_url_param: true, initial_language_from_url_param_to_localstorage: true, diff --git a/examples/ssr-hydrate-actix/src/app.rs b/examples/ssr-hydrate-actix/src/app.rs index ba6578a6..1a94aba2 100644 --- a/examples/ssr-hydrate-actix/src/app.rs +++ b/examples/ssr-hydrate-actix/src/app.rs @@ -19,6 +19,7 @@ pub fn App() -> impl IntoView { locales: "./locales", check_translations: "./src/**/*.rs", cookie_name: "lang", + cookie_attrs: "SameSite=Strict; Secure; path=/; max-age=600", initial_language_from_cookie: true, set_language_to_cookie: true, url_param: "lang", diff --git a/examples/ssr-hydrate-axum/src/app.rs b/examples/ssr-hydrate-axum/src/app.rs index 63bdab8b..4fab9005 100644 --- a/examples/ssr-hydrate-axum/src/app.rs +++ b/examples/ssr-hydrate-axum/src/app.rs @@ -21,6 +21,7 @@ pub fn App() -> impl IntoView { locales: "./locales", check_translations: "./src/**/*.rs", cookie_name: "lang", + cookie_attrs: "SameSite=Strict; Secure; path=/; max-age=600", initial_language_from_cookie: true, set_language_to_cookie: true, url_param: "lang", diff --git a/leptos-fluent-macros/Cargo.toml b/leptos-fluent-macros/Cargo.toml index f0faa22a..b5dc53bb 100644 --- a/leptos-fluent-macros/Cargo.toml +++ b/leptos-fluent-macros/Cargo.toml @@ -2,7 +2,7 @@ name = "leptos-fluent-macros" description = "Macros for leptos-fluent" edition.workspace = true -version = "0.0.36" +version = "0.0.37" license = "MIT" documentation.workspace = true repository.workspace = true diff --git a/leptos-fluent-macros/src/lib.rs b/leptos-fluent-macros/src/lib.rs index d5a0bf49..2eece431 100644 --- a/leptos-fluent-macros/src/lib.rs +++ b/leptos-fluent-macros/src/lib.rs @@ -146,6 +146,9 @@ use quote::quote; /// runtime. It will only take effect on server-side. /// - **`cookie_name`** (_`"lf-lang"`_): The cookie name to manage language in a cookie. Can be a literal string or an /// expression that will be evaluated at runtime. It will take effect on client-side and server side. +/// - **`cookie_attrs`** (_`""`_): The [attributes][cookie-attributes] to set in the cookie. Can be a literal string or an expression +/// that will be evaluated at runtime. For example, `"SameSite=Strict; Secure; path=/; max-age=600"`. +/// It will take effect on client-side. /// - **`initial_language_from_cookie`** (_`false`_): Load the initial language of the user from a cookie. /// Can be a literal boolean or an expression that will be evaluated at runtime. It will take effect on client-side /// and server side. @@ -159,6 +162,7 @@ use quote::quote; /// [local storage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage /// [`navigator.languages`]: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages /// [`leptos::create_effect`]: https://docs.rs/leptos/latest/leptos/fn.create_effect.html +/// [cookie-attributes]: https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#write_a_new_cookie #[proc_macro] pub fn leptos_fluent( input: proc_macro::TokenStream, @@ -190,6 +194,8 @@ pub fn leptos_fluent( initial_language_from_accept_language_header_expr, cookie_name_str, cookie_name_expr, + cookie_attrs_str, + cookie_attrs_expr, initial_language_from_cookie_bool, initial_language_from_cookie_expr, set_language_to_cookie_bool, @@ -647,11 +653,20 @@ pub fn leptos_fluent( #[cfg(not(feature = "ssr"))] let sync_language_with_cookie_quote = { + let cookie_attrs = match cookie_attrs_str { + Some(lit) => quote! { #lit }, + None => match cookie_attrs_expr { + Some(expr) => quote! { #expr }, + None => quote! { "" }, + }, + }; + let effect_quote = quote! { ::leptos::create_effect(move |_| { ::leptos_fluent::cookie::set( #cookie_name, &::leptos_fluent::expect_i18n().language.get().id.to_string(), + &#cookie_attrs, ); }); }; diff --git a/leptos-fluent-macros/src/loader.rs b/leptos-fluent-macros/src/loader.rs index 25414116..15992ad4 100644 --- a/leptos-fluent-macros/src/loader.rs +++ b/leptos-fluent-macros/src/loader.rs @@ -103,6 +103,8 @@ pub(crate) struct I18nLoader { Option, pub(crate) cookie_name_str: Option, pub(crate) cookie_name_expr: Option, + pub(crate) cookie_attrs_str: Option, + pub(crate) cookie_attrs_expr: Option, pub(crate) initial_language_from_cookie_bool: Option, pub(crate) initial_language_from_cookie_expr: Option, pub(crate) set_language_to_cookie_bool: Option, @@ -158,6 +160,8 @@ impl Parse for I18nLoader { > = None; let mut cookie_name_str: Option = None; let mut cookie_name_expr: Option = None; + let mut cookie_attrs_str: Option = None; + let mut cookie_attrs_expr: Option = None; let mut initial_language_from_cookie_bool: Option = None; let mut initial_language_from_cookie_expr: Option = None; let mut set_language_to_cookie_bool: Option = None; @@ -284,6 +288,15 @@ impl Parse for I18nLoader { ) { return Err(err); } + } else if k == "cookie_attrs" { + if let Some(err) = parse_litstr_or_expr_param( + &fields, + &mut cookie_attrs_str, + &mut cookie_attrs_expr, + "cookie_attrs", + ) { + return Err(err); + } } else if k == "initial_language_from_cookie" { if let Some(err) = parse_litbool_or_expr_param( &fields, @@ -493,6 +506,8 @@ impl Parse for I18nLoader { initial_language_from_accept_language_header_expr, cookie_name_str, cookie_name_expr, + cookie_attrs_str, + cookie_attrs_expr, initial_language_from_cookie_bool, initial_language_from_cookie_expr, set_language_to_cookie_bool, diff --git a/leptos-fluent/Cargo.toml b/leptos-fluent/Cargo.toml index 0f57d973..5410c34f 100644 --- a/leptos-fluent/Cargo.toml +++ b/leptos-fluent/Cargo.toml @@ -2,7 +2,7 @@ name = "leptos-fluent" description = "Fluent framework for internationalization of Leptos applications" edition.workspace = true -version = "0.0.36" +version = "0.0.37" license = "MIT" documentation.workspace = true repository.workspace = true diff --git a/leptos-fluent/README.md b/leptos-fluent/README.md index c5e62815..4c939409 100644 --- a/leptos-fluent/README.md +++ b/leptos-fluent/README.md @@ -20,7 +20,7 @@ Add the following to your `Cargo.toml` file: ```toml [dependencies] -leptos-fluent = "0.0.36" +leptos-fluent = "0.0.37" fluent-templates = "0.9" [features] @@ -117,6 +117,11 @@ fn App() -> impl IntoView { // Get the initial language from `navigator.languages` if not // found in the local storage. By default, it is `false`. initial_language_from_navigator: true, + // Attributes to set for the language cookie. By default is `""`. + cookie_attrs: "SameSite=Strict; Secure; path=/; max-age=600", + // Update the language on cookie when using the method `I18n.set_language`. + // By default, it is `false`. + set_language_to_cookie: true, // Server side options // ------------------- @@ -131,9 +136,6 @@ fn App() -> impl IntoView { cookie_name: "lang", // Get the initial language from cookie. By default, it is `false`. initial_language_from_cookie: true, - // Update the language on cookie when using the method `I18n.set_language`. - // By default, it is `false`. - set_language_to_cookie: true, // URL parameter name to use discovering the initial language // of the user. By default is `"lang"`. url_param: "lang", diff --git a/leptos-fluent/src/cookie.rs b/leptos-fluent/src/cookie.rs index c3247c12..6ee526f6 100644 --- a/leptos-fluent/src/cookie.rs +++ b/leptos-fluent/src/cookie.rs @@ -21,36 +21,41 @@ pub fn get(name: &str) -> Option { } } -pub fn set(name: &str, value: &str) { +#[cfg(not(feature = "ssr"))] +fn set_cookie(new_value: &str) { + use wasm_bindgen::JsCast; + leptos::document() + .dyn_into::() + .unwrap() + .set_cookie(new_value) + .unwrap(); +} + +pub fn set(name: &str, value: &str, attrs: &str) { #[cfg(not(feature = "ssr"))] { - use wasm_bindgen::JsCast; - leptos::document() - .dyn_into::() - .unwrap() - .set_cookie(&format!("{}={}", name, value)) - .unwrap(); + let mut new_value = format!("{}={}", name, value); + if !attrs.is_empty() { + new_value.push_str("; "); + new_value.push_str(attrs); + } + set_cookie(&new_value); } #[cfg(feature = "ssr")] { _ = name; _ = value; + _ = attrs; } } pub fn delete(name: &str) { #[cfg(not(feature = "ssr"))] { - use wasm_bindgen::JsCast; - leptos::document() - .dyn_into::() - .unwrap() - .set_cookie(&format!( - "{}=; expires=Thu, 01 Jan 1970 00:00:00 GMT", - name - )) - .unwrap(); + let new_value = + format!("{}=; expires=Thu, 01 Jan 1970 00:00:00 GMT", name); + set_cookie(&new_value); } #[cfg(feature = "ssr")] diff --git a/leptos-fluent/src/lib.rs b/leptos-fluent/src/lib.rs index 5c85b635..bc8ec6ff 100644 --- a/leptos-fluent/src/lib.rs +++ b/leptos-fluent/src/lib.rs @@ -15,7 +15,7 @@ //! //! ```toml //! [dependencies] -//! leptos-fluent = "0.0.36" +//! leptos-fluent = "0.0.37" //! fluent-templates = "0.9" //! //! [features] @@ -112,6 +112,11 @@ //! // Get the initial language from `navigator.languages` if not //! // found in the local storage. By default, it is `false`. //! initial_language_from_navigator: true, +//! // Attributes to set for the language cookie. By default is `""`. +//! cookie_attrs: "SameSite=Strict; Secure; path=/; max-age=600", +//! // Update the language on cookie when using the method `I18n.set_language`. +//! // By default, it is `false`. +//! set_language_to_cookie: true, //! //! // Server side options //! // ------------------- @@ -126,9 +131,6 @@ //! cookie_name: "lang", //! // Get the initial language from cookie. By default, it is `false`. //! initial_language_from_cookie: true, -//! // Update the language on cookie when using the method `I18n.set_language`. -//! // By default, it is `false`. -//! set_language_to_cookie: true, //! // URL parameter name to use discovering the initial language //! // of the user. By default is `"lang"`. //! url_param: "lang",