pub const DEFAULT_LANGUAGE: &str = "en"; const SUPPORTED_LANGUAGES: &[&str] = &["en", "fr", "de", "zh", "hi", "hu"]; /// The set of language codes the frontend can render, used e.g. to emit /// `hreflang` alternates for indexable pages. pub fn supported_languages() -> &'static [&'static str] { SUPPORTED_LANGUAGES } pub fn supported_language(value: &str) -> Option<&'static str> { let value = value.trim().to_ascii_lowercase(); if value.is_empty() || value == "*" { return None; } let primary = value.split('-').next().unwrap_or(""); SUPPORTED_LANGUAGES .iter() .copied() .find(|language| value == *language || primary == *language) } pub fn language_from_accept_language(header: Option<&str>) -> &'static str { let Some(header) = header else { return DEFAULT_LANGUAGE; }; let mut best_language = None; let mut best_quality = 0.0f32; for item in header.split(',') { let mut parts = item.split(';'); let tag = parts.next().unwrap_or("").trim(); let mut quality = 1.0f32; for param in parts { let param = param.trim(); if let Some(value) = param.strip_prefix("q=") { quality = value.parse::().unwrap_or(0.0); } } if quality <= 0.0 { continue; } if let Some(language) = supported_language(tag) { if quality > best_quality { best_language = Some(language); best_quality = quality; } } } best_language.unwrap_or(DEFAULT_LANGUAGE) } pub fn query_string_with_language(query_string: &str, language: &str) -> String { if url::form_urlencoded::parse(query_string.as_bytes()).any(|(key, _)| key == "lang") { return query_string.to_string(); } if query_string.is_empty() { format!("lang={language}") } else { format!("{query_string}&lang={language}") } } #[cfg(test)] mod tests { use super::*; #[test] fn maps_browser_language_tags_to_supported_languages() { assert_eq!(supported_language("fr-CA"), Some("fr")); assert_eq!(supported_language("zh-Hans-CN"), Some("zh")); assert_eq!(supported_language("es"), None); } #[test] fn chooses_highest_quality_supported_language() { assert_eq!( language_from_accept_language(Some("en-US;q=0.6, fr-FR;q=0.9, de;q=0.7")), "fr" ); assert_eq!( language_from_accept_language(Some("es-ES, hi-IN;q=0.8")), "hi" ); assert_eq!(language_from_accept_language(Some("es-ES")), "en"); } #[test] fn appends_language_without_overriding_explicit_query_language() { assert_eq!(query_string_with_language("", "de"), "lang=de"); assert_eq!( query_string_with_language("lat=51.5&lon=-0.1", "de"), "lat=51.5&lon=-0.1&lang=de" ); assert_eq!( query_string_with_language("lang=fr&lat=1", "de"), "lang=fr&lat=1" ); } }