rust 58 lines · 7 steps

A locale extractor for Axum handlers

Parse the Accept-Language header into a supported locale and inject it as a custom Axum extractor.

Explained by highlit
1use axum::{
2 extract::FromRequestParts,
3 http::{request::Parts, StatusCode, header::ACCEPT_LANGUAGE},
4};
5 
6const SUPPORTED: &[&str] = &["en", "fr", "de", "es", "ja"];
7const DEFAULT_LOCALE: &str = "en";
8 
9#[derive(Debug, Clone)]
10pub struct Locale(pub String);
11 
12impl Locale {
13 fn negotiate(header: &str) -> String {
14 let mut ranked: Vec<(f32, &str)> = header
15 .split(',')
16 .filter_map(|part| {
17 let mut segments = part.trim().split(';');
18 let tag = segments.next()?.trim();
19 let primary = tag.split('-').next()?.to_ascii_lowercase();
20 if primary.is_empty() {
21 return None;
22 }
23 let quality = segments
24 .find_map(|s| s.trim().strip_prefix("q="))
25 .and_then(|q| q.parse::<f32>().ok())
26 .unwrap_or(1.0);
27 SUPPORTED
28 .iter()
29 .find(|s| **s == primary)
30 .map(|s| (quality, *s))
31 })
32 .collect();
33 
34 ranked.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
35 ranked
36 .first()
37 .map(|(_, tag)| tag.to_string())
38 .unwrap_or_else(|| DEFAULT_LOCALE.to_string())
39 }
40}
41 
42impl<S> FromRequestParts<S> for Locale
43where
44 S: Send + Sync,
45{
46 type Rejection = (StatusCode, &'static str);
47 
48 async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
49 let locale = parts
50 .headers
51 .get(ACCEPT_LANGUAGE)
52 .and_then(|value| value.to_str().ok())
53 .map(Self::negotiate)
54 .unwrap_or_else(|| DEFAULT_LOCALE.to_string());
55 
56 Ok(Locale(locale))
57 }
58}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Implementing FromRequestParts turns any parsing logic into a reusable handler argument.
  2. 2Accept-Language negotiation means ranking client preferences by q-value against a fixed supported set.
  3. 3Falling back to a default locale at every failure point keeps the extractor infallible in practice.

Related explainers

Share this explainer

Here's the card — post it anywhere.

A locale extractor for Axum handlers — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code