1 // This file is part of ICU4X. For terms of use, please see the file 2 // called LICENSE at the top level of the ICU4X source tree 3 // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). 4 5 #[cfg(feature = "alloc")] 6 use crate::subtags::Variants; 7 use crate::subtags::{Language, Region, Script, Subtag, Variant}; 8 use crate::DataLocale; 9 10 /// The structure storing locale subtags used in preferences. 11 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 12 pub struct LocalePreferences { 13 /// Preference of Language 14 pub(crate) language: Language, 15 /// Preference of Script 16 pub(crate) script: Option<Script>, 17 /// Preference of Region 18 pub(crate) region: Option<Region>, 19 /// Preference of Variant 20 pub(crate) variant: Option<Variant>, 21 /// Preference of Regional Subdivision 22 pub(crate) subdivision: Option<Subtag>, 23 /// Preference of Unicode Extension Region 24 pub(crate) ue_region: Option<Region>, 25 } 26 27 impl LocalePreferences { to_data_locale_maybe_region_priority(self, region_priority: bool) -> DataLocale28 fn to_data_locale_maybe_region_priority(self, region_priority: bool) -> DataLocale { 29 DataLocale { 30 language: self.language, 31 script: self.script, 32 region: match (self.region, self.ue_region) { 33 (Some(_), Some(r)) if region_priority => Some(r), 34 (r, _) => r, 35 }, 36 variant: self.variant, 37 subdivision: self.subdivision, 38 } 39 } 40 41 /// Convert to a DataLocale, with region-based fallback priority 42 /// 43 /// Most users should use `icu_provider::marker::make_locale()` instead. to_data_locale_region_priority(self) -> DataLocale44 pub fn to_data_locale_region_priority(self) -> DataLocale { 45 self.to_data_locale_maybe_region_priority(true) 46 } 47 48 /// Convert to a DataLocale, with language-based fallback priority 49 /// 50 /// Most users should use `icu_provider::marker::make_locale()` instead. to_data_locale_language_priority(self) -> DataLocale51 pub fn to_data_locale_language_priority(self) -> DataLocale { 52 self.to_data_locale_maybe_region_priority(false) 53 } 54 } 55 impl Default for LocalePreferences { default() -> Self56 fn default() -> Self { 57 Self::default() 58 } 59 } 60 61 impl From<&crate::Locale> for LocalePreferences { from(loc: &crate::Locale) -> Self62 fn from(loc: &crate::Locale) -> Self { 63 let sd = loc 64 .extensions 65 .unicode 66 .keywords 67 .get(&crate::extensions::unicode::key!("sd")) 68 .and_then(|v| v.as_single_subtag().copied()); 69 let ue_region = loc 70 .extensions 71 .unicode 72 .keywords 73 .get(&crate::extensions::unicode::key!("rg")) 74 .and_then(|v| { 75 v.as_single_subtag() 76 .and_then(|s| Region::try_from_str(s.as_str()).ok()) 77 }); 78 Self { 79 language: loc.id.language, 80 script: loc.id.script, 81 region: loc.id.region, 82 variant: loc.id.variants.iter().copied().next(), 83 subdivision: sd, 84 ue_region, 85 } 86 } 87 } 88 89 impl From<&crate::LanguageIdentifier> for LocalePreferences { from(lid: &crate::LanguageIdentifier) -> Self90 fn from(lid: &crate::LanguageIdentifier) -> Self { 91 Self { 92 language: lid.language, 93 script: lid.script, 94 region: lid.region, 95 variant: lid.variants.iter().copied().next(), 96 subdivision: None, 97 ue_region: None, 98 } 99 } 100 } 101 102 #[cfg(feature = "alloc")] 103 impl From<LocalePreferences> for crate::Locale { from(prefs: LocalePreferences) -> Self104 fn from(prefs: LocalePreferences) -> Self { 105 Self { 106 id: crate::LanguageIdentifier { 107 language: prefs.language, 108 script: prefs.script, 109 region: prefs.region, 110 variants: prefs 111 .variant 112 .map(Variants::from_variant) 113 .unwrap_or_default(), 114 }, 115 extensions: { 116 let mut extensions = crate::extensions::Extensions::default(); 117 if let Some(sd) = prefs.subdivision { 118 extensions.unicode.keywords.set( 119 crate::extensions::unicode::key!("sd"), 120 crate::extensions::unicode::Value::from_subtag(Some(sd)), 121 ); 122 } 123 if let Some(rg) = prefs.ue_region { 124 #[allow(clippy::unwrap_used)] // Region is a valid Subtag 125 extensions.unicode.keywords.set( 126 crate::extensions::unicode::key!("rg"), 127 crate::extensions::unicode::Value::try_from_str(rg.as_str()).unwrap(), 128 ); 129 } 130 extensions 131 }, 132 } 133 } 134 } 135 136 impl LocalePreferences { 137 /// Constructs a new [`LocalePreferences`] struct with the defaults. default() -> Self138 pub const fn default() -> Self { 139 Self { 140 language: Language::default(), 141 script: None, 142 region: None, 143 variant: None, 144 subdivision: None, 145 ue_region: None, 146 } 147 } 148 149 /// Preference of Language language(&self) -> Language150 pub const fn language(&self) -> Language { 151 self.language 152 } 153 154 /// Extends the preferences with the values from another set of preferences. extend(&mut self, other: LocalePreferences)155 pub fn extend(&mut self, other: LocalePreferences) { 156 if !other.language.is_default() { 157 self.language = other.language; 158 } 159 if let Some(script) = other.script { 160 self.script = Some(script); 161 } 162 if let Some(region) = other.region { 163 self.region = Some(region); 164 } 165 if let Some(variant) = other.variant { 166 self.variant = Some(variant); 167 } 168 if let Some(sd) = other.subdivision { 169 self.subdivision = Some(sd); 170 } 171 if let Some(ue_region) = other.ue_region { 172 self.ue_region = Some(ue_region); 173 } 174 } 175 } 176