107 lines
3 KiB
Rust
107 lines
3 KiB
Rust
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::<f32>().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"
|
|
);
|
|
}
|
|
}
|