pub fn bcp47_language(iso639_2: &str) -> Option<String> {
let code = iso639_2.trim().to_ascii_lowercase();
if code.len() != 3 || !code.bytes().all(|b| b.is_ascii_lowercase()) {
return None;
}
if code == "und" || code == "mul" || code == "mis" || code == "zxx" {
return Some(code);
}
if code.starts_with('q') {
let bytes = code.as_bytes();
if (b'a'..=b't').contains(&bytes[1]) && bytes[2].is_ascii_lowercase() {
return Some(code);
}
}
let lang = iso_639::Language::from_str(&code)?;
if let Some(a2) = lang.alpha2() {
return Some(a2.to_string());
}
if let Some(t) = lang.alpha3_t() {
return Some(t.to_string());
}
lang.alpha3_b().map(|b| b.to_string())
}
#[must_use]
pub fn language_matches(preferred: &str, language: &str) -> bool {
let (p, l) = (
preferred.to_ascii_lowercase(),
language.to_ascii_lowercase(),
);
l == p || l.strip_prefix(&p).is_some_and(|rest| rest.starts_with('-'))
}
#[cfg(test)]
mod tests_lang_match {
use super::*;
#[test]
fn exact_and_prefix() {
assert!(language_matches("en", "en"));
assert!(language_matches("en", "en-US"));
assert!(!language_matches("en", "fr"));
assert!(!language_matches("en", "english"));
}
}