icu_locale_core/preferences/
locale.rs

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")]
6use crate::subtags::Variants;
7use crate::subtags::{Language, Region, Script, Subtag, Variant};
8use crate::DataLocale;
9
10/// The structure storing locale subtags used in preferences.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub 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
27impl LocalePreferences {
28    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.
44    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.
51    pub fn to_data_locale_language_priority(self) -> DataLocale {
52        self.to_data_locale_maybe_region_priority(false)
53    }
54}
55impl Default for LocalePreferences {
56    fn default() -> Self {
57        Self::default()
58    }
59}
60
61impl From<&crate::Locale> for LocalePreferences {
62    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
89impl From<&crate::LanguageIdentifier> for LocalePreferences {
90    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")]
103impl From<LocalePreferences> for crate::Locale {
104    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
136impl LocalePreferences {
137    /// Constructs a new [`LocalePreferences`] struct with the defaults.
138    pub const fn default() -> Self {
139        Self {
140            language: Language::UNKNOWN,
141            script: None,
142            region: None,
143            variant: None,
144            subdivision: None,
145            ue_region: None,
146        }
147    }
148
149    /// Preference of Language
150    pub const fn language(&self) -> Language {
151        self.language
152    }
153
154    /// Preference of Region
155    pub const fn region(&self) -> Option<Region> {
156        self.region
157    }
158
159    /// Extends the preferences with the values from another set of preferences.
160    pub fn extend(&mut self, other: LocalePreferences) {
161        if !other.language.is_unknown() {
162            self.language = other.language;
163        }
164        if let Some(script) = other.script {
165            self.script = Some(script);
166        }
167        if let Some(region) = other.region {
168            self.region = Some(region);
169        }
170        if let Some(variant) = other.variant {
171            self.variant = Some(variant);
172        }
173        if let Some(sd) = other.subdivision {
174            self.subdivision = Some(sd);
175        }
176        if let Some(ue_region) = other.ue_region {
177            self.ue_region = Some(ue_region);
178        }
179    }
180}