From 5222c131bef0c8702b11586c09cdde1a340e7527 Mon Sep 17 00:00:00 2001 From: Arjen Date: Fri, 26 Jul 2024 15:03:33 +0200 Subject: [PATCH 01/22] Introduce language variable --- wallet_core/mock_relying_party/src/app.rs | 13 +++++++++++++ .../templates/usecase/mijn_amsterdam.askama | 1 + .../templates/usecase/monkey_bike.askama | 1 + .../templates/usecase/online_marketplace.askama | 1 + .../templates/usecase/usecase.askama | 7 ++++++- .../templates/usecase/xyz_bank.askama | 1 + 6 files changed, 23 insertions(+), 1 deletion(-) diff --git a/wallet_core/mock_relying_party/src/app.rs b/wallet_core/mock_relying_party/src/app.rs index 6f6a07df1..7641903ef 100644 --- a/wallet_core/mock_relying_party/src/app.rs +++ b/wallet_core/mock_relying_party/src/app.rs @@ -142,11 +142,20 @@ struct SessionResponse { session_token: SessionToken, } +#[derive(Default, Serialize, Deserialize, strum::Display)] +#[strum(serialize_all = "kebab-case")] +enum Language { + #[default] + NL, + EN, +} + #[derive(Template, Serialize)] #[template(path = "disclosed/attributes.askama", escape = "html", ext = "html")] struct DisclosureTemplate<'a> { usecase: &'a str, attributes: DisclosedAttributes, + language: Language, } #[derive(Template, Serialize)] @@ -157,6 +166,7 @@ struct UsecaseTemplate<'a> { wallet_web_filename: &'a str, wallet_web_sha256: &'a str, error: Option<&'a str>, + language: Language, } #[derive(Debug, Serialize, Deserialize)] @@ -218,6 +228,7 @@ async fn usecase(State(state): State>, Path(usecase): Path wallet_web_filename: &state.wallet_web.filename.to_string_lossy(), wallet_web_sha256: &state.wallet_web.sha256, error: None, + language: Language::default(), }; Ok(askama_axum::into_response(&result)) @@ -242,6 +253,7 @@ async fn disclosed_attributes( let result = DisclosureTemplate { usecase: &usecase, attributes, + language: Language::default(), }; Ok(askama_axum::into_response(&result)) } @@ -253,6 +265,7 @@ async fn disclosed_attributes( wallet_web_filename: &state.wallet_web.filename.to_string_lossy(), wallet_web_sha256: &state.wallet_web.sha256, error: Some(&err), + language: Language::default(), }; Ok(askama_axum::into_response(&result)) } diff --git a/wallet_core/mock_relying_party/templates/usecase/mijn_amsterdam.askama b/wallet_core/mock_relying_party/templates/usecase/mijn_amsterdam.askama index 500b3b915..9f2da81ff 100644 --- a/wallet_core/mock_relying_party/templates/usecase/mijn_amsterdam.askama +++ b/wallet_core/mock_relying_party/templates/usecase/mijn_amsterdam.askama @@ -19,6 +19,7 @@ text="Inloggen met NL Wallet" usecase="{{ usecase }}" base-url="../" + lang="{{ language }}" > diff --git a/wallet_core/mock_relying_party/templates/usecase/monkey_bike.askama b/wallet_core/mock_relying_party/templates/usecase/monkey_bike.askama index aaa3dd4aa..cf4be92ad 100644 --- a/wallet_core/mock_relying_party/templates/usecase/monkey_bike.askama +++ b/wallet_core/mock_relying_party/templates/usecase/monkey_bike.askama @@ -15,6 +15,7 @@ text="Verder met NL Wallet" usecase="{{ usecase }}" base-url="../" + lang="{{ language }}" > diff --git a/wallet_core/mock_relying_party/templates/usecase/online_marketplace.askama b/wallet_core/mock_relying_party/templates/usecase/online_marketplace.askama index 392f829e9..12adf7a4d 100644 --- a/wallet_core/mock_relying_party/templates/usecase/online_marketplace.askama +++ b/wallet_core/mock_relying_party/templates/usecase/online_marketplace.askama @@ -15,6 +15,7 @@ text="Verder met NL Wallet" usecase="{{ usecase }}" base-url="../" + lang="{{ language }}"> > diff --git a/wallet_core/mock_relying_party/templates/usecase/usecase.askama b/wallet_core/mock_relying_party/templates/usecase/usecase.askama index 6e5598096..8215bca10 100644 --- a/wallet_core/mock_relying_party/templates/usecase/usecase.askama +++ b/wallet_core/mock_relying_party/templates/usecase/usecase.askama @@ -24,7 +24,12 @@ {% when "xyz_bank" %} {% include "xyz_bank.askama" %} {% else %} - + + {% endmatch %} {# should be last for accessibility purposes #} diff --git a/wallet_core/mock_relying_party/templates/usecase/xyz_bank.askama b/wallet_core/mock_relying_party/templates/usecase/xyz_bank.askama index 000ebe2e3..02efc0bba 100644 --- a/wallet_core/mock_relying_party/templates/usecase/xyz_bank.askama +++ b/wallet_core/mock_relying_party/templates/usecase/xyz_bank.askama @@ -16,6 +16,7 @@ text="Gebruik NL Wallet" usecase="{{ usecase }}" base-url="../" + lang="{{ language }}"> > Kies een ander middel From a99fbef0f0e9e93271efdeebda4c108be226ab8b Mon Sep 17 00:00:00 2001 From: Arjen Date: Fri, 26 Jul 2024 16:31:30 +0200 Subject: [PATCH 02/22] Implement and style language picker --- .../assets/css/demo_bar.css | 160 ++++++++++++++---- .../assets/non-free/images/down.svg | 1 + wallet_core/mock_relying_party/src/app.rs | 30 +++- .../templates/components/demo_bar.askama | 25 ++- .../templates/disclosed/attributes.askama | 2 +- .../templates/usecase/usecase.askama | 2 +- 6 files changed, 173 insertions(+), 47 deletions(-) create mode 100644 wallet_core/mock_relying_party/assets/non-free/images/down.svg diff --git a/wallet_core/mock_relying_party/assets/css/demo_bar.css b/wallet_core/mock_relying_party/assets/css/demo_bar.css index 7adcce5ee..5ecf9b28e 100644 --- a/wallet_core/mock_relying_party/assets/css/demo_bar.css +++ b/wallet_core/mock_relying_party/assets/css/demo_bar.css @@ -1,53 +1,153 @@ aside { - order: -1; + order: -1; - width: 100vw; - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - gap: 16px; - padding: 16px; - background: #f2f1fe; - color: #152a62; + width: 100vw; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 16px; + gap: 16px; + background: #f2f1fe; + color: #152a62; } @media screen and (min-width: 500px) { - aside { - padding: 8px 24px; - } + aside { + padding: 8px 24px 8px 94px; /* 24 + 16 + 54 */ + } } aside b { - font-weight: 700; + font-weight: 700; } aside a { - color: #383ede; + color: #383ede; } aside a:hover { - color: #3237c4; - text-decoration: none; + color: #3237c4; + text-decoration: none; } -aside::before { - content: " "; - background: url("../non-free/images/nl-wallet.svg") no-repeat center center / cover; - width: 40px; - height: 40px; +aside .demo-bar { + flex-grow: 0; + display: flex; + justify-content: center; + align-items: center; + gap: 16px; +} + +aside .demo-bar::before { + content: " "; + background: url("../non-free/images/nl-wallet.svg") no-repeat center center / cover; + width: 40px; + height: 40px; } aside .text { - display: flex; - flex-direction: column; - justify-content: center; + display: flex; + flex-direction: column; + justify-content: center; } @media screen and (min-width: 500px) { - aside .text { - flex-direction: row; - align-items: center; - gap: 8px; - } + aside { + justify-content: right; + } + + aside .demo-bar { + flex-grow: 1; + } + + aside .text { + flex-direction: row; + align-items: center; + gap: 8px; + } +} + +.lang-selector { + position: relative; +} + +.lang-selector label[for="lang_toggle"] { + display: flex; + align-items: center; + + padding: 4px 8px; + gap: 4px; + border-radius: 2px; + + background: #fcfcfc; + color: #383ede; + font-weight: 700; + text-transform: uppercase; + + user-select: none; +} + +.lang-selector label[for="lang_toggle"]:hover { + background-color: #f1f1f1; + cursor: pointer; +} + +.lang-selector label[for="lang_toggle"]::after { + content: " "; + background: url("../non-free/images/down.svg") no-repeat center center / contain; + + width: 16px; + height: 16px; +} + +#lang_toggle { + display: none; +} + +#lang_toggle:checked + .lang-modal { + display: block; +} + +.lang-selector .lang-modal { + position: absolute; + right: 0; + z-index: 1; + + margin-top: 2px; + + background: #fcfcfc; + box-shadow: 0px 4px 40px 0px #00000029; + border-radius: 2px; + + overflow: hidden; + + display: none; +} + +.lang-selector .lang-modal button { + display: flex; + padding: 12px 24px 12px 12px; + gap: 12px; + color: #152a62; + width: 100%; +} + +.lang-selector .lang-modal button:not(:disabled):hover { + background-color: #f1f1f1; + cursor: pointer; +} + +.lang-selector .lang-modal button::before { + content: " "; + width: 24px; + height: 24px; +} + +.lang-selector .lang-modal button:disabled::before { + content: " "; + background-color: #152a62; + mask: url("../non-free/images/checkmark.svg") no-repeat center center / contain; + width: 24px; + height: 24px; } diff --git a/wallet_core/mock_relying_party/assets/non-free/images/down.svg b/wallet_core/mock_relying_party/assets/non-free/images/down.svg new file mode 100644 index 000000000..2b79d995a --- /dev/null +++ b/wallet_core/mock_relying_party/assets/non-free/images/down.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wallet_core/mock_relying_party/src/app.rs b/wallet_core/mock_relying_party/src/app.rs index 7641903ef..d301ac776 100644 --- a/wallet_core/mock_relying_party/src/app.rs +++ b/wallet_core/mock_relying_party/src/app.rs @@ -19,6 +19,7 @@ use axum::{ use base64::prelude::*; use http::{header::CACHE_CONTROL, HeaderValue}; use serde::{Deserialize, Serialize}; +use strum::IntoEnumIterator; use tower::ServiceBuilder; use tower_http::{ cors::{Any, CorsLayer}, @@ -142,12 +143,13 @@ struct SessionResponse { session_token: SessionToken, } -#[derive(Default, Serialize, Deserialize, strum::Display)] +#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, strum::Display, strum::EnumIter)] +#[serde(rename_all = "kebab-case")] #[strum(serialize_all = "kebab-case")] -enum Language { +pub enum Language { #[default] - NL, - EN, + Nl, + En, } #[derive(Template, Serialize)] @@ -173,6 +175,12 @@ struct UsecaseTemplate<'a> { pub struct DisclosedAttributesParams { pub nonce: Option, pub session_token: SessionToken, + pub lang: Option, +} + +#[derive(Debug, Deserialize)] +pub struct UsecaseParams { + pub lang: Option, } async fn create_session( @@ -217,7 +225,11 @@ async fn create_session( static USECASE_JS_SHA256: LazyLock = LazyLock::new(|| BASE64_STANDARD.encode(sha256(include_bytes!("../assets/usecase.js")))); -async fn usecase(State(state): State>, Path(usecase): Path) -> Result { +async fn usecase( + State(state): State>, + Path(usecase): Path, + Query(params): Query, +) -> Result { if !state.usecases.contains_key(&usecase) { return Ok(StatusCode::NOT_FOUND.into_response()); } @@ -228,7 +240,7 @@ async fn usecase(State(state): State>, Path(usecase): Path wallet_web_filename: &state.wallet_web.filename.to_string_lossy(), wallet_web_sha256: &state.wallet_web.sha256, error: None, - language: Language::default(), + language: params.lang.unwrap_or_default(), }; Ok(askama_axum::into_response(&result)) @@ -248,12 +260,14 @@ async fn disclosed_attributes( .disclosed_attributes(params.session_token, params.nonce) .await; + let language = params.lang.unwrap_or_default(); + match attributes { Ok(attributes) => { let result = DisclosureTemplate { usecase: &usecase, attributes, - language: Language::default(), + language, }; Ok(askama_axum::into_response(&result)) } @@ -265,7 +279,7 @@ async fn disclosed_attributes( wallet_web_filename: &state.wallet_web.filename.to_string_lossy(), wallet_web_sha256: &state.wallet_web.sha256, error: Some(&err), - language: Language::default(), + language, }; Ok(askama_axum::into_response(&result)) } diff --git a/wallet_core/mock_relying_party/templates/components/demo_bar.askama b/wallet_core/mock_relying_party/templates/components/demo_bar.askama index 30be1b18c..6c366f211 100644 --- a/wallet_core/mock_relying_party/templates/components/demo_bar.askama +++ b/wallet_core/mock_relying_party/templates/components/demo_bar.askama @@ -1,11 +1,22 @@ -{% macro demo_bar(text, url, url_text) %} +{% macro demo_bar(text, url, url_text, selected_lang) %} {% endmacro %} diff --git a/wallet_core/mock_relying_party/templates/disclosed/attributes.askama b/wallet_core/mock_relying_party/templates/disclosed/attributes.askama index e9e94d7ca..7b796829b 100644 --- a/wallet_core/mock_relying_party/templates/disclosed/attributes.askama +++ b/wallet_core/mock_relying_party/templates/disclosed/attributes.askama @@ -26,5 +26,5 @@ {% endmatch %} {# should be last for accessibility purposes #} -{% call demo_bar::demo_bar("Bekijk andere", "../", "voorbeelden") %} +{% call demo_bar::demo_bar("Bekijk andere", "../", "voorbeelden", language) %} {% endblock %} diff --git a/wallet_core/mock_relying_party/templates/usecase/usecase.askama b/wallet_core/mock_relying_party/templates/usecase/usecase.askama index 8215bca10..abd06e725 100644 --- a/wallet_core/mock_relying_party/templates/usecase/usecase.askama +++ b/wallet_core/mock_relying_party/templates/usecase/usecase.askama @@ -33,5 +33,5 @@ {% endmatch %} {# should be last for accessibility purposes #} -{% call demo_bar::demo_bar("Volg de ontwikkelingen op", "https://edi.pleio.nl", "edi.pleio.nl") %} +{% call demo_bar::demo_bar("Volg de ontwikkelingen op", "https://edi.pleio.nl", "edi.pleio.nl", language) %} {% endblock %} From 3dfa16be4f6fa3ff13367aa4739049764498e251 Mon Sep 17 00:00:00 2001 From: Arjen Date: Tue, 30 Jul 2024 17:01:29 +0200 Subject: [PATCH 03/22] Add basic static dictionary implementation --- wallet_core/mock_relying_party/src/app.rs | 14 +++++-- wallet_core/mock_relying_party/src/lib.rs | 1 + .../mock_relying_party/src/translations.rs | 41 +++++++++++++++++++ .../templates/components/demo_bar.askama | 4 +- .../templates/disclosed/attributes.askama | 2 +- .../templates/usecase/usecase.askama | 2 +- 6 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 wallet_core/mock_relying_party/src/translations.rs diff --git a/wallet_core/mock_relying_party/src/app.rs b/wallet_core/mock_relying_party/src/app.rs index d301ac776..875347e1e 100644 --- a/wallet_core/mock_relying_party/src/app.rs +++ b/wallet_core/mock_relying_party/src/app.rs @@ -36,6 +36,7 @@ use crate::{ askama_axum, client::WalletServerClient, settings::{Origin, ReturnUrlMode, Settings, Usecase, WalletWeb}, + translations::{Words, TRANSLATIONS}, }; #[derive(Debug)] @@ -143,7 +144,7 @@ struct SessionResponse { session_token: SessionToken, } -#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, strum::Display, strum::EnumIter)] +#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Hash, strum::Display, strum::EnumIter)] #[serde(rename_all = "kebab-case")] #[strum(serialize_all = "kebab-case")] pub enum Language { @@ -158,6 +159,7 @@ struct DisclosureTemplate<'a> { usecase: &'a str, attributes: DisclosedAttributes, language: Language, + t: &'a Words<'a>, } #[derive(Template, Serialize)] @@ -169,6 +171,7 @@ struct UsecaseTemplate<'a> { wallet_web_sha256: &'a str, error: Option<&'a str>, language: Language, + t: &'a Words<'a>, } #[derive(Debug, Serialize, Deserialize)] @@ -234,13 +237,16 @@ async fn usecase( return Ok(StatusCode::NOT_FOUND.into_response()); } + let language = params.lang.unwrap_or_default(); + let t = TRANSLATIONS.get(&language).unwrap(); // TODO unwrap? let result = UsecaseTemplate { usecase: &usecase, usecase_js_sha256: &USECASE_JS_SHA256, wallet_web_filename: &state.wallet_web.filename.to_string_lossy(), wallet_web_sha256: &state.wallet_web.sha256, error: None, - language: params.lang.unwrap_or_default(), + language, + t, }; Ok(askama_axum::into_response(&result)) @@ -261,13 +267,14 @@ async fn disclosed_attributes( .await; let language = params.lang.unwrap_or_default(); - + let t = TRANSLATIONS.get(&language).unwrap(); // TODO unwrap? match attributes { Ok(attributes) => { let result = DisclosureTemplate { usecase: &usecase, attributes, language, + t, }; Ok(askama_axum::into_response(&result)) } @@ -280,6 +287,7 @@ async fn disclosed_attributes( wallet_web_sha256: &state.wallet_web.sha256, error: Some(&err), language, + t, }; Ok(askama_axum::into_response(&result)) } diff --git a/wallet_core/mock_relying_party/src/lib.rs b/wallet_core/mock_relying_party/src/lib.rs index 3334a2c61..1a16bb231 100644 --- a/wallet_core/mock_relying_party/src/lib.rs +++ b/wallet_core/mock_relying_party/src/lib.rs @@ -2,6 +2,7 @@ pub mod app; pub mod client; pub mod server; pub mod settings; +pub mod translations; // workaround for: https://github.com/djc/askama/issues/810#issuecomment-1494522435 pub mod askama_axum; diff --git a/wallet_core/mock_relying_party/src/translations.rs b/wallet_core/mock_relying_party/src/translations.rs new file mode 100644 index 000000000..0d196205f --- /dev/null +++ b/wallet_core/mock_relying_party/src/translations.rs @@ -0,0 +1,41 @@ +use std::{collections::HashMap, ops::Index, sync::LazyLock}; + +use serde::Serialize; + +use crate::app::Language; + +pub type Translations<'a> = HashMap>; + +pub static TRANSLATIONS: LazyLock = LazyLock::new(|| { + let nl = Words { + en: "English", + nl: "Nederlands", + }; + + let en = Words { + en: "English", + nl: "Nederlands", + }; + + let mut translations = HashMap::new(); + translations.insert(Language::Nl, nl); + translations.insert(Language::En, en); + translations +}); + +#[derive(Serialize)] +pub struct Words<'a> { + en: &'a str, + nl: &'a str, +} + +impl<'a> Index for Words<'a> { + type Output = &'a str; + + fn index(&self, lang: Language) -> &Self::Output { + match lang { + Language::Nl => &self.nl, + Language::En => &self.en, + } + } +} diff --git a/wallet_core/mock_relying_party/templates/components/demo_bar.askama b/wallet_core/mock_relying_party/templates/components/demo_bar.askama index 6c366f211..133d24804 100644 --- a/wallet_core/mock_relying_party/templates/components/demo_bar.askama +++ b/wallet_core/mock_relying_party/templates/components/demo_bar.askama @@ -1,4 +1,4 @@ -{% macro demo_bar(text, url, url_text, selected_lang) %} +{% macro demo_bar(text, url, url_text, selected_lang, t) %}