diff options
Diffstat (limited to 'server/src/routes')
-rw-r--r-- | server/src/routes/locale.rs | 42 | ||||
-rw-r--r-- | server/src/routes/ui/layout.rs | 62 |
2 files changed, 61 insertions, 43 deletions
diff --git a/server/src/routes/locale.rs b/server/src/routes/locale.rs index adce685..6d16c17 100644 --- a/server/src/routes/locale.rs +++ b/server/src/routes/locale.rs @@ -30,26 +30,26 @@ impl<'r> FromRequest<'r> for AcceptLanguage { 'life0: 'async_trait, Self: 'async_trait, { - Box::pin(async move { - Outcome::Success(AcceptLanguage( - request - .headers() - .get_one("accept-language") - .and_then(|h| { - h.split(",") - .filter_map(|e| { - let code = e.split(";").next()?; - let code = code.split_once("-").unwrap_or((code, "")).0; - match code { - "en" => Some(Language::English), - "de" => Some(Language::German), - _ => None, - } - }) - .next() - }) - .unwrap_or(Language::English), - )) - }) + Box::pin(async move { Outcome::Success(AcceptLanguage(lang_from_request(request))) }) } } + +pub(crate) fn lang_from_request(request: &Request) -> Language { + request + .headers() + .get_one("accept-language") + .and_then(|h| { + h.split(",") + .filter_map(|e| { + let code = e.split(";").next()?; + let code = code.split_once("-").unwrap_or((code, "")).0; + match code { + "en" => Some(Language::English), + "de" => Some(Language::German), + _ => None, + } + }) + .next() + }) + .unwrap_or(Language::English) +} diff --git a/server/src/routes/ui/layout.rs b/server/src/routes/ui/layout.rs index f05fcb2..aea5b4a 100644 --- a/server/src/routes/ui/layout.rs +++ b/server/src/routes/ui/layout.rs @@ -4,22 +4,28 @@ Copyright (C) 2025 metamuffin <metamuffin.org> */ use crate::{ - routes::ui::{ - account::{ - rocket_uri_macro_r_account_login, rocket_uri_macro_r_account_logout, - rocket_uri_macro_r_account_register, session::Session, - settings::rocket_uri_macro_r_account_settings, + routes::{ + locale::lang_from_request, + ui::{ + account::{ + rocket_uri_macro_r_account_login, rocket_uri_macro_r_account_logout, + rocket_uri_macro_r_account_register, session::Session, + settings::rocket_uri_macro_r_account_settings, + }, + admin::rocket_uri_macro_r_admin_dashboard, + browser::rocket_uri_macro_r_all_items, + node::rocket_uri_macro_r_library_node, + search::rocket_uri_macro_r_search, + stats::rocket_uri_macro_r_stats, }, - admin::rocket_uri_macro_r_admin_dashboard, - browser::rocket_uri_macro_r_all_items, - node::rocket_uri_macro_r_library_node, - search::rocket_uri_macro_r_search, - stats::rocket_uri_macro_r_stats, }, uri, }; use futures::executor::block_on; -use jellybase::CONF; +use jellybase::{ + locale::{tr, Language}, + CONF, +}; use jellycommon::user::Theme; use jellycommon::NodeID; use jellyimport::is_importing; @@ -29,12 +35,22 @@ use rocket::{ response::{self, Responder}, Request, Response, }; -use std::{io::Cursor, sync::LazyLock}; +use std::{borrow::Cow, io::Cursor, sync::LazyLock}; static LOGO_ENABLED: LazyLock<bool> = LazyLock::new(|| CONF.asset_path.join("logo.svg").exists()); +pub struct TrString<'a>(Cow<'a, str>); +impl Render for TrString<'_> { + fn render(&self, writer: &mut impl std::fmt::Write) -> std::fmt::Result { + self.0.as_str().render(writer) + } +} +pub fn trs<'a>(lang: &Language, key: &str) -> TrString<'a> { + TrString(tr(*lang, key, &[])) +} + markup::define! { - Layout<'a, Main: Render>(title: String, main: Main, class: &'a str, session: Option<Session>) { + Layout<'a, Main: Render>(title: String, main: Main, class: &'a str, session: Option<Session>, lang: Language) { @markup::doctype() html { head { @@ -47,23 +63,23 @@ markup::define! { nav { h1 { a[href=if session.is_some() {"/home"} else {"/"}] { @if *LOGO_ENABLED { img.logo[src="/assets/logo.svg"]; } else { @CONF.brand } } } " " @if let Some(_) = session { - a.library[href=uri!(r_library_node("library"))] { "My Library" } " " - a.library[href=uri!(r_all_items())] { "All Items" } " " - a.library[href=uri!(r_search(None::<&'static str>, None::<usize>))] { "Search" } " " - a.library[href=uri!(r_stats())] { "Stats" } " " + a.library[href=uri!(r_library_node("library"))] { @trs(lang, "nav.root") } " " + a.library[href=uri!(r_all_items())] { @trs(lang, "nav.all") } " " + a.library[href=uri!(r_search(None::<&'static str>, None::<usize>))] { @trs(lang, "nav.search") } " " + a.library[href=uri!(r_stats())] { @trs(lang, "nav.stats") } " " } @if is_importing() { span.warn { "Library database is updating..." } } div.account { @if let Some(session) = session { span { "Logged in as " } span.username { @session.user.display_name } " " @if session.user.admin { - a.admin.hybrid_button[href=uri!(r_admin_dashboard())] { p {"Administration"} } " " + a.admin.hybrid_button[href=uri!(r_admin_dashboard())] { p {@trs(lang, "nav.admin")} } " " } - a.settings.hybrid_button[href=uri!(r_account_settings())] { p {"Settings"} } " " - a.logout.hybrid_button[href=uri!(r_account_logout())] { p {"Log out"} } + a.settings.hybrid_button[href=uri!(r_account_settings())] { p {@trs(lang, "nav.settings")} } " " + a.logout.hybrid_button[href=uri!(r_account_logout())] { p {@trs(lang, "nav.logout")} } } else { - a.register.hybrid_button[href=uri!(r_account_register())] { p {"Register"} } " " - a.login.hybrid_button[href=uri!(r_account_login())] { p {"Log in"} } + a.register.hybrid_button[href=uri!(r_account_register())] { p {@trs(lang, "nav.register")} } " " + a.login.hybrid_button[href=uri!(r_account_login())] { p {@trs(lang, "nav.login")} } } } } @@ -108,6 +124,7 @@ impl<'r, Main: Render> Responder<'r, 'static> for LayoutPage<Main> { // TODO blocking the event loop here. it seems like there is no other way to // TODO offload this, since the guard references `req` which has a lifetime. // TODO therefore we just block. that is fine since the database is somewhat fast. + let lang = lang_from_request(&req); let session = block_on(req.guard::<Option<Session>>()).unwrap(); let mut out = String::new(); Layout { @@ -122,6 +139,7 @@ impl<'r, Main: Render> Responder<'r, 'static> for LayoutPage<Main> { .unwrap_or(Theme::Dark) ), session, + lang, } .render(&mut out) .unwrap(); |