use std::sync::LazyLock;
use regex::Regex;
use crate::validation::data_errors::DataValidationError;
static LANGUAGE_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"^[a-zA-Z]{2,3}(-[a-zA-Z0-9]{1,8})*$").expect("language regex should compile")
});
pub fn validate_language(value: &str) -> Result<(), DataValidationError> {
if value.is_empty() {
return Err(DataValidationError::StringFormatInvalid {
format: "language".to_string(),
value: value.to_string(),
reason: "language tag cannot be empty".to_string(),
});
}
if !LANGUAGE_REGEX.is_match(value) {
return Err(DataValidationError::StringFormatInvalid {
format: "language".to_string(),
value: value.to_string(),
reason: "language tag must be a valid BCP 47 tag".to_string(),
});
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_language_tags() {
let valid = [
"en",
"en-US",
"pt-BR",
"zh-Hans",
"zh-Hans-CN",
"de",
"fr-FR",
"ja",
"ko",
];
for lang in valid {
assert!(validate_language(lang).is_ok(), "should be valid: {}", lang);
}
}
#[test]
fn test_invalid_language_tags() {
let invalid = ["", "e", "123", "en_US", "toolongsubtag-exceeds"];
for lang in invalid {
assert!(
validate_language(lang).is_err(),
"should be invalid: {}",
lang
);
}
}
}