icu_locale_core/preferences/
locale.rs1use crate::extensions::unicode::{SubdivisionId, SubdivisionSuffix};
6use crate::preferences::extensions::unicode::keywords::{RegionOverride, RegionalSubdivision};
7#[cfg(feature = "alloc")]
8use crate::subtags::Variants;
9use crate::subtags::{Language, Region, Script, Variant};
10use crate::DataLocale;
11
12#[derive(#[automatically_derived]
impl ::core::fmt::Debug for LocalePreferences {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field5_finish(f,
"LocalePreferences", "language", &self.language, "script",
&self.script, "region", &self.region, "variant", &self.variant,
"region_override", &&self.region_override)
}
}Debug, #[automatically_derived]
impl ::core::clone::Clone for LocalePreferences {
#[inline]
fn clone(&self) -> LocalePreferences {
let _: ::core::clone::AssertParamIsClone<Language>;
let _: ::core::clone::AssertParamIsClone<Option<Script>>;
let _: ::core::clone::AssertParamIsClone<Option<RegionalSubdivision>>;
let _: ::core::clone::AssertParamIsClone<Option<Variant>>;
let _: ::core::clone::AssertParamIsClone<Option<RegionOverride>>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for LocalePreferences { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for LocalePreferences {
#[inline]
fn eq(&self, other: &LocalePreferences) -> bool {
self.language == other.language && self.script == other.script &&
self.region == other.region && self.variant == other.variant
&& self.region_override == other.region_override
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for LocalePreferences {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<Language>;
let _: ::core::cmp::AssertParamIsEq<Option<Script>>;
let _: ::core::cmp::AssertParamIsEq<Option<RegionalSubdivision>>;
let _: ::core::cmp::AssertParamIsEq<Option<Variant>>;
let _: ::core::cmp::AssertParamIsEq<Option<RegionOverride>>;
}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for LocalePreferences {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.language, state);
::core::hash::Hash::hash(&self.script, state);
::core::hash::Hash::hash(&self.region, state);
::core::hash::Hash::hash(&self.variant, state);
::core::hash::Hash::hash(&self.region_override, state)
}
}Hash)]
14pub struct LocalePreferences {
15 pub(crate) language: Language,
17 pub(crate) script: Option<Script>,
19 pub(crate) region: Option<RegionalSubdivision>,
21 pub(crate) variant: Option<Variant>,
23 pub(crate) region_override: Option<RegionOverride>,
25}
26
27impl LocalePreferences {
28 pub const fn to_data_locale_region_priority(self) -> DataLocale {
32 DataLocale::from_parts(
33 self.language,
34 self.script,
35 if let Some(region) = self.region_override {
36 Some(region.0)
37 } else if let Some(region) = self.region {
38 Some(region.0)
39 } else {
40 None
41 },
42 self.variant,
43 )
44 }
45
46 pub const fn to_data_locale_language_priority(self) -> DataLocale {
50 DataLocale::from_parts(
51 self.language,
52 self.script,
53 if let Some(region) = self.region {
54 Some(region.0)
55 } else {
56 None
57 },
58 self.variant,
59 )
60 }
61}
62impl Default for LocalePreferences {
63 fn default() -> Self {
64 Self::default()
65 }
66}
67
68impl From<&crate::Locale> for LocalePreferences {
69 fn from(loc: &crate::Locale) -> Self {
70 Self::from_locale_strict(loc).unwrap_or_else(|e| e)
71 }
72}
73
74impl From<&crate::LanguageIdentifier> for LocalePreferences {
75 fn from(lid: &crate::LanguageIdentifier) -> Self {
76 Self {
77 language: lid.language,
78 script: lid.script,
79 region: lid.region.map(|region| {
80 RegionalSubdivision(SubdivisionId {
81 region,
82 suffix: SubdivisionSuffix::UNKNOWN,
83 })
84 }),
85 variant: lid.variants.iter().copied().next(),
86 region_override: None,
87 }
88 }
89}
90
91#[cfg(feature = "alloc")]
93impl From<LocalePreferences> for crate::Locale {
94 fn from(prefs: LocalePreferences) -> Self {
95 Self {
96 id: crate::LanguageIdentifier {
97 language: prefs.language,
98 script: prefs.script,
99 region: prefs.region.map(|sd| sd.region),
100 variants: prefs
101 .variant
102 .map(Variants::from_variant)
103 .unwrap_or_default(),
104 },
105 extensions: {
106 let mut extensions = crate::extensions::Extensions::default();
107 if let Some(sd) = prefs.region.filter(|sd| !sd.suffix.is_unknown()) {
108 extensions
109 .unicode
110 .keywords
111 .set(RegionalSubdivision::UNICODE_EXTENSION_KEY, sd.into());
112 }
113 if let Some(rg) = prefs.region_override {
114 extensions
115 .unicode
116 .keywords
117 .set(RegionOverride::UNICODE_EXTENSION_KEY, rg.into());
118 }
119 extensions
120 },
121 }
122 }
123}
124
125impl LocalePreferences {
126 pub const fn default() -> Self {
128 Self {
129 language: Language::UNKNOWN,
130 script: None,
131 region: None,
132 variant: None,
133 region_override: None,
134 }
135 }
136
137 pub fn from_locale_strict(loc: &crate::Locale) -> Result<Self, Self> {
141 let mut is_err = false;
142
143 let subdivision = if let Some(sd) = loc
144 .extensions
145 .unicode
146 .keywords
147 .get(&RegionalSubdivision::UNICODE_EXTENSION_KEY)
148 {
149 if let Ok(sd) = RegionalSubdivision::try_from(sd) {
150 Some(sd)
151 } else {
152 is_err = true;
153 None
154 }
155 } else {
156 None
157 };
158
159 let region = if let Some(sd) = subdivision {
160 if let Some(region) = loc.id.region {
161 Some(RegionalSubdivision(SubdivisionId {
163 region,
164 suffix: if sd.region == region {
165 sd.suffix
166 } else {
167 is_err = true;
168 SubdivisionSuffix::UNKNOWN
169 },
170 }))
171 } else {
172 Some(sd)
174 }
175 } else {
176 loc.id.region.map(|region| {
177 RegionalSubdivision(SubdivisionId {
178 region,
179 suffix: SubdivisionSuffix::UNKNOWN,
180 })
181 })
182 };
183 let region_override = loc
184 .extensions
185 .unicode
186 .keywords
187 .get(&RegionOverride::UNICODE_EXTENSION_KEY)
188 .and_then(|v| {
189 RegionOverride::try_from(v)
190 .inspect_err(|_| is_err = true)
191 .ok()
192 });
193
194 (if is_err { Err } else { Ok })(Self {
195 language: loc.id.language,
196 script: loc.id.script,
197 region,
198 variant: loc.id.variants.iter().copied().next(),
199 region_override,
200 })
201 }
202
203 #[deprecated(since = "2.2.0", note = "convert to `DataLocale` to access fields")]
205 pub const fn language(&self) -> Language {
206 self.to_data_locale_language_priority().language
207 }
208
209 #[deprecated(since = "2.2.0", note = "convert to `DataLocale` to access fields")]
211 pub const fn region(&self) -> Option<Region> {
212 self.to_data_locale_region_priority().region
213 }
214
215 pub fn extend(&mut self, other: LocalePreferences) {
217 if !other.language.is_unknown() {
218 self.language = other.language;
219 }
220 if let Some(script) = other.script {
221 self.script = Some(script);
222 }
223 if let Some(sd) = other.region {
224 if !sd.suffix.is_unknown() || Some(sd.region) != self.region.map(|sd| sd.region) {
226 self.region = Some(sd);
227 }
228 }
229 if let Some(variant) = other.variant {
230 self.variant = Some(variant);
231 }
232 if let Some(region_override) = other.region_override {
233 self.region_override = Some(region_override);
234 }
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241 use crate::Locale;
242
243 #[test]
244 fn test_data_locale_conversion() {
245 #[derive(Debug)]
246 struct TestCase<'a> {
247 input: &'a str,
248 language_priority: &'a str,
249 region_priority: &'a str,
250 }
251 let test_cases = [
252 TestCase {
253 input: "en",
254 language_priority: "en",
255 region_priority: "en",
256 },
257 TestCase {
258 input: "en-US",
259 language_priority: "en-US",
260 region_priority: "en-US",
261 },
262 TestCase {
263 input: "en-u-sd-ustx",
264 language_priority: "en-US-u-sd-ustx",
265 region_priority: "en-US-u-sd-ustx",
266 },
267 TestCase {
268 input: "en-US-u-sd-ustx",
269 language_priority: "en-US-u-sd-ustx",
270 region_priority: "en-US-u-sd-ustx",
271 },
272 TestCase {
273 input: "en-u-rg-gbzzzz",
274 language_priority: "en",
275 region_priority: "en-GB",
276 },
277 TestCase {
278 input: "en-US-u-rg-gbzzzz",
279 language_priority: "en-US",
280 region_priority: "en-GB",
281 },
282 TestCase {
283 input: "!en-US-u-sd-gbzzzz",
284 language_priority: "en-US",
285 region_priority: "en-US",
286 },
287 TestCase {
288 input: "en-u-rg-gbzzzz-sd-ustx",
289 language_priority: "en-US-u-sd-ustx",
290 region_priority: "en-GB",
291 },
292 TestCase {
293 input: "en-US-u-rg-gbzzzz-sd-ustx",
294 language_priority: "en-US-u-sd-ustx",
295 region_priority: "en-GB",
296 },
297 TestCase {
298 input: "en-US-u-rg-gbeng-sd-ustx",
299 language_priority: "en-US-u-sd-ustx",
300 region_priority: "en-GB-u-sd-gbeng",
301 },
302 TestCase {
303 input: "!en-TR-u-rg-true",
304 language_priority: "en-TR",
305 region_priority: "en-TR",
306 },
307 TestCase {
308 input: "!en-US-u-sd-tx",
309 language_priority: "en-US",
310 region_priority: "en-US",
311 },
312 TestCase {
313 input: "!en-GB-u-rg-tx",
314 language_priority: "en-GB",
315 region_priority: "en-GB",
316 },
317 TestCase {
318 input: "en-US-u-rg-eng",
319 language_priority: "en-US",
320 region_priority: "en-EN-u-sd-eng",
321 },
322 TestCase {
323 input: "!en-001-u-sd-001",
327 language_priority: "en-001",
328 region_priority: "en-001",
329 },
330 ];
331 for test_case in test_cases.iter() {
332 let prefs = if let Some(locale) = test_case.input.strip_prefix("!") {
333 LocalePreferences::from_locale_strict(&Locale::try_from_str(locale).unwrap())
334 .expect_err(locale)
335 } else {
336 LocalePreferences::from_locale_strict(
337 &Locale::try_from_str(test_case.input).unwrap(),
338 )
339 .expect(test_case.input)
340 };
341 assert_eq!(
342 prefs.to_data_locale_language_priority().to_string(),
343 test_case.language_priority,
344 "{test_case:?}"
345 );
346 assert_eq!(
347 prefs.to_data_locale_region_priority().to_string(),
348 test_case.region_priority,
349 "{test_case:?}"
350 );
351 }
352 }
353}