use crate::config::Config;
use crate::http::Response;
use crate::middleware::{Middleware, Next};
use crate::Request;
use async_trait::async_trait;
use ferro_lang::{normalize_locale, LangConfig};
use super::{locale_scope, with_locale_scope};
pub struct LangMiddleware;
#[async_trait]
impl Middleware for LangMiddleware {
async fn handle(&self, request: Request, next: Next) -> Response {
let config = Config::get::<LangConfig>().unwrap_or_default();
let detected = detect_locale(&request, &config);
let ctx = locale_scope();
{
let mut guard = ctx.write().await;
*guard = Some(detected);
}
with_locale_scope(ctx, async { next(request).await }).await
}
}
fn detect_locale(request: &Request, config: &LangConfig) -> String {
if let Some(locale) = request.query("locale") {
if !locale.is_empty() {
return normalize_locale(&locale);
}
}
if let Some(accept) = request.header("accept-language") {
if let Some(locale) = parse_accept_language(accept) {
return locale;
}
}
normalize_locale(&config.locale)
}
fn parse_accept_language(header: &str) -> Option<String> {
let first = header.split(',').next()?.trim();
if first.is_empty() {
return None;
}
let lang = first.split(';').next()?.trim();
if lang.is_empty() || lang == "*" {
return None;
}
Some(normalize_locale(lang))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_accept_language_full_header() {
let result = parse_accept_language("en-US,en;q=0.9,fr;q=0.8");
assert_eq!(result, Some("en-us".to_string()));
}
#[test]
fn parse_accept_language_single_tag() {
let result = parse_accept_language("fr");
assert_eq!(result, Some("fr".to_string()));
}
#[test]
fn parse_accept_language_with_quality() {
let result = parse_accept_language("de-DE;q=0.8");
assert_eq!(result, Some("de-de".to_string()));
}
#[test]
fn parse_accept_language_normalizes_underscore() {
let result = parse_accept_language("pt_BR,en;q=0.5");
assert_eq!(result, Some("pt-br".to_string()));
}
#[test]
fn parse_accept_language_empty() {
let result = parse_accept_language("");
assert_eq!(result, None);
}
#[test]
fn parse_accept_language_wildcard() {
let result = parse_accept_language("*");
assert_eq!(result, None);
}
#[test]
fn parse_accept_language_trims_whitespace() {
let result = parse_accept_language(" es-MX , en;q=0.5 ");
assert_eq!(result, Some("es-mx".to_string()));
}
}