use std::str::FromStr; use poem::{ error::I18NError, http::header, i18n::{unic_langid::LanguageIdentifier, I18NArgs, I18NResources}, session::Session, Endpoint, FromRequest, Middleware, Request, RequestBody, }; use serde::Deserialize; #[derive(Deserialize)] struct Params { lang: String, } pub async fn set_language(next: E, mut req: Request) -> poem::Result { if let Ok(params) = req.params::() { let session = req .extensions() .get::() .expect("To use the `set_language` middleware, the `Session` data is required."); session.set("lang", params.lang); println!("{:?}", session.get::("lang")) } next.call(req).await } pub struct Locale(poem::i18n::I18NBundle); impl Locale { /// Gets the text with arguments. /// /// See also: [`I18NBundle::text_with_args`](I18NBundle::text_with_args) pub fn text_with_args<'a>( &self, id: impl AsRef, args: impl Into>, ) -> Result { self.0.text_with_args(id, args) } /// Gets the text. /// /// See also: [`I18NBundle::text`](I18NBundle::text) pub fn text(&self, id: impl AsRef) -> Result { self.0.text(id) } } #[poem::async_trait] impl<'a> FromRequest<'a> for Locale { async fn from_request(req: &'a Request, body: &mut RequestBody) -> poem::Result { let session = req .extensions() .get::() .expect("To use the `Locale` extractor, the `Session` data is required."); let resources = req .extensions() .get::() .expect("To use the `Locale` extractor, the `I18NResources` data is required."); let mut lang_id = None; if let Some(lang) = session.get::("lang") { lang_id = Some(lang); }; if let Some(lang_id) = lang_id { if let Ok(lang_id) = LanguageIdentifier::from_str(&lang_id) { return Ok(Self(resources.negotiate_languages(&[&lang_id]))); } }; let accept_languages = req .headers() .get(header::ACCEPT_LANGUAGE) .and_then(|value| value.to_str().ok()) .map(parse_accept_languages) .unwrap_or_default(); Ok(Self(resources.negotiate_languages(&accept_languages))) } } fn parse_accept_languages(value: &str) -> Vec { let mut languages = Vec::new(); for s in value.split(',').map(str::trim) { if let Some(res) = parse_language(s) { languages.push(res); } } languages.sort_by(|(_, a), (_, b)| b.cmp(a)); languages .into_iter() .map(|(language, _)| language) .collect() } fn parse_language(value: &str) -> Option<(LanguageIdentifier, u16)> { let mut parts = value.split(';'); let name = parts.next()?.trim(); let quality = match parts.next() { Some(quality) => parse_quality(quality).unwrap_or_default(), None => 1000, }; let language = LanguageIdentifier::from_str(name).ok()?; Some((language, quality)) } fn parse_quality(value: &str) -> Option { let mut parts = value.split('='); let name = parts.next()?.trim(); if name != "q" { return None; } let q = parts.next()?.trim().parse::().ok()?; Some((q.clamp(0.0, 1.0) * 1000.0) as u16) }