diff --git a/src/index.rs b/src/index.rs index 274041d..b030ebd 100644 --- a/src/index.rs +++ b/src/index.rs @@ -1,7 +1,10 @@ -use crate::page::new; +use crate::{language::language, page::new}; +use axum::http::HeaderMap; +use axum_extra::extract::CookieJar; use maud::{html, Markup}; -pub(super) async fn index() -> Markup { +pub(super) async fn index(cookies: CookieJar, headers: HeaderMap) -> Markup { + let lang = language(&cookies, &headers); new(html! { h1 { "Digital Shadows" } hgroup { diff --git a/src/language.rs b/src/language.rs new file mode 100644 index 0000000..f30f1da --- /dev/null +++ b/src/language.rs @@ -0,0 +1,33 @@ +use crate::Language; +use axum::http::HeaderMap; +use axum_extra::extract::CookieJar; + +pub(crate) fn language(cookies: &CookieJar, headers: &HeaderMap) -> Language { + if let Some(lang_cookie) = cookies.clone().get("language") { + // Return existing language cookie + lang_cookie.value().to_string().into() + } else { + // Extract language from Accept-Language header + extract_preferred_language(headers).into() + } +} + +fn extract_preferred_language(headers: &HeaderMap) -> String { + headers + .get("accept-language") + .and_then(|header| header.to_str().ok()) + .and_then(|accept_lang| { + // Parse Accept-Language header (format: "en-US,en;q=0.9,fr;q=0.8") + accept_lang + .split(',') + .next() // Get first (highest priority) language + .map(|lang| { + lang.split(';') + .next() // Remove quality factor (q=0.9) + .unwrap_or(lang) + .trim() + .to_lowercase() + }) + }) + .unwrap_or_else(|| "en".to_string()) // Default to English +} diff --git a/src/main.rs b/src/main.rs index 2e7f7d5..6738cdc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ use crate::model::client::Client; -use axum::{routing::get, Router}; +use axum::{http::HeaderMap, routing::get, Router}; use axum_extra::extract::{cookie::Cookie, CookieJar}; use sqlx::{pool::PoolOptions, sqlite::SqliteConnectOptions, SqlitePool}; use std::{str::FromStr, sync::Arc}; @@ -8,6 +8,7 @@ use uuid::Uuid; mod game; mod index; +pub(crate) mod language; pub(crate) mod model; mod page; pub(crate) mod random_names; @@ -16,6 +17,27 @@ pub(crate) enum Backend { Sqlite(SqlitePool), } +#[derive(Debug)] +enum Language { + German, + English, +} + +impl From for Language { + fn from(value: String) -> Self { + if value.starts_with("de") { + Language::German + } else { + Language::English + } + } +} + +struct Req { + client: Client, + lang: Language, +} + impl Backend { async fn client(&self, cookies: CookieJar) -> (CookieJar, Client) { let existing_uuid = cookies @@ -31,6 +53,19 @@ impl Backend { } } } + + // Combined method for getting both client and language + async fn client_full(&self, cookies: CookieJar, headers: &HeaderMap) -> (CookieJar, Req) { + let (cookies, client) = self.client(cookies).await; + let lang = language::language(&cookies, headers); + ( + cookies, + Req { + client, + lang: lang.into(), + }, + ) + } } #[tokio::main]