use crate::global;
use crate::service::HttpRequest;
use super::{LangId, LanguageIdentifier, Locale};
pub struct RequestLocale {
base: &'static LanguageIdentifier,
effective: &'static LanguageIdentifier,
}
impl RequestLocale {
pub fn from_request(request: Option<&HttpRequest>) -> Self {
let mode = global::SETTINGS.app.lang_negotiation;
let base: &'static LanguageIdentifier = match mode {
global::LangNegotiation::ConfigOnly => {
Locale::default_langid()
}
global::LangNegotiation::Full | global::LangNegotiation::NoQuery => {
if let Some(default) = Locale::configured_langid() {
default
} else {
request
.and_then(|req| req.headers().get("Accept-Language"))
.and_then(|value| value.to_str().ok())
.and_then(|header| {
let first = header.split(',').next()?.trim();
let tag = first.split(';').next()?.trim();
if tag.is_empty() {
None
} else if let Locale::Resolved(langid) = Locale::resolve(tag) {
Some(langid)
} else {
None
}
})
.unwrap_or(Locale::fallback_langid())
}
}
};
let effective: &'static LanguageIdentifier = match mode {
global::LangNegotiation::ConfigOnly | global::LangNegotiation::NoQuery => {
base
}
global::LangNegotiation::Full => {
request
.and_then(|req| {
req.query_string().split('&').find_map(|pair| {
let mut param = pair.splitn(2, '=');
match (param.next(), param.next()) {
(Some("lang"), Some(value)) if !value.is_empty() => Some(value),
_ => None,
}
})
})
.and_then(|language| {
if let Locale::Resolved(langid) = Locale::resolve(language) {
Some(langid)
} else {
None
}
})
.unwrap_or(base)
}
};
RequestLocale { base, effective }
}
#[inline]
pub fn with_langid(&mut self, language: &impl LangId) -> &mut Self {
self.effective = language.langid();
self
}
#[inline]
pub(crate) fn needs_lang_query(&self) -> bool {
match global::SETTINGS.app.lang_negotiation {
global::LangNegotiation::Full => self.base != self.effective,
global::LangNegotiation::NoQuery | global::LangNegotiation::ConfigOnly => false,
}
}
}
impl LangId for RequestLocale {
#[inline]
fn langid(&self) -> &'static LanguageIdentifier {
self.effective
}
}