mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-08-05 02:38:44 +02:00
refactor: ui refresh
This commit is contained in:
@@ -9,6 +9,7 @@ use rand::RngExt;
|
||||
use std::collections::HashMap;
|
||||
use std::net::IpAddr;
|
||||
use std::str::FromStr;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
const TERRITORY_INFO_XML: &str = include_str!("territory_info.xml");
|
||||
|
||||
@@ -41,6 +42,27 @@ pub enum GeolocationError {
|
||||
Ip(#[from] IpError),
|
||||
}
|
||||
|
||||
/// The language part of a locale or language tag: `"en"` from `"en-US"`,
|
||||
/// `"zh"` from `"zh_Hant"`.
|
||||
pub fn primary_subtag(tag: &str) -> &str {
|
||||
tag.split(['-', '_']).next().unwrap_or(tag)
|
||||
}
|
||||
|
||||
/// Shared selector over the bundled CLDR territory data. Parsing the XML costs
|
||||
/// real time, and the data is immutable, so build it once.
|
||||
pub fn locale_selector() -> Option<&'static LocaleSelector> {
|
||||
static SELECTOR: OnceLock<Option<LocaleSelector>> = OnceLock::new();
|
||||
SELECTOR
|
||||
.get_or_init(|| match LocaleSelector::new() {
|
||||
Ok(s) => Some(s),
|
||||
Err(e) => {
|
||||
log::warn!("Failed to build the CLDR locale selector: {e}");
|
||||
None
|
||||
}
|
||||
})
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Locale {
|
||||
pub language: String,
|
||||
@@ -152,6 +174,28 @@ impl LocaleSelector {
|
||||
Ok(Self { territories })
|
||||
}
|
||||
|
||||
/// Whether CLDR associates `language` with `region` at all.
|
||||
///
|
||||
/// Returns `None` for a territory with no language data, so callers can skip
|
||||
/// rather than guess. The test is deliberately "is this language listed for
|
||||
/// this country", not "is this the country's main language": `from_region`
|
||||
/// samples the whole listed distribution weighted by speaker share, so every
|
||||
/// listed language is one this selector itself can emit. Anything stricter
|
||||
/// would flag fingerprints Donut generated on purpose — CLDR puts `es` at
|
||||
/// 9.6% in the US and `fr` at 30% in Canada, and those get picked.
|
||||
pub fn region_speaks(&self, region: &str, language: &str) -> Option<bool> {
|
||||
let languages = self.territories.get(®ion.to_uppercase())?;
|
||||
if languages.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let primary = primary_subtag(language);
|
||||
Some(
|
||||
languages
|
||||
.iter()
|
||||
.any(|l| primary_subtag(&l.language).eq_ignore_ascii_case(primary)),
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub fn from_region(&self, region: &str) -> Result<Locale, GeolocationError> {
|
||||
let region_upper = region.to_uppercase();
|
||||
|
||||
Reference in New Issue
Block a user