rust_macios/foundation/
ns_locale.rs

1use objc::{msg_send, sel, sel_impl};
2
3use crate::{
4    object,
5    objective_c_runtime::{
6        id,
7        macros::interface_impl,
8        nil,
9        traits::{FromId, PNSObject},
10    },
11    utils::to_bool,
12};
13
14use super::{
15    NSArray, NSCharacterSet, NSCoder, NSDictionary, NSLocaleKey, NSNotificationName, NSString,
16};
17
18/// The directions that a language may take across a page of text.
19#[repr(usize)]
20#[derive(Debug)]
21pub enum LanguageDirection {
22    /// The direction of the language is unknown.
23    Unknown = 0,
24    /// The language direction is from left to right.
25    LeftToRight = 1,
26    /// The language direction is from right to left.
27    RightToLeft = 2,
28    /// The language direction is from top to bottom.
29    TopToBottom = 3,
30    /// The language direction is from bottom to top.
31    BottomToTop = 4,
32}
33
34object! {
35    /// The [`NSLocale`] class provides information about the user’s locale and formatting preferences.
36    unsafe pub struct NSLocale;
37}
38
39#[interface_impl(NSObject)]
40impl NSLocale {
41    /* Initializing a Locale
42     */
43
44    /// Returns a locale initialized using the given locale identifier.
45    #[method]
46    pub fn locale_with_locale_identifier(string: &NSString) -> Self
47    where
48        Self: Sized + FromId,
49    {
50        unsafe {
51            let ptr = msg_send![Self::m_class(), localeWithLocaleIdentifier: string.m_self()];
52            FromId::from_id(ptr)
53        }
54    }
55
56    /// Initializes a locale using a given locale identifier.
57    #[method]
58    pub fn init_with_locale_identifier(&mut self, locale_identifier: &NSString) -> Self
59    where
60        Self: Sized + FromId,
61    {
62        unsafe {
63            let ptr =
64                msg_send![self.m_self(), initWithLocaleIdentifier: locale_identifier.m_self()];
65            FromId::from_id(ptr)
66        }
67    }
68
69    /// Returns a locale initialized from data in the given unarchiver.
70    #[method]
71    pub fn init_with_coder(&mut self, coder: &NSCoder) -> Self
72    where
73        Self: Sized + FromId,
74    {
75        unsafe { Self::from_id(msg_send![self.m_self(), initWithCoder: coder.m_self()]) }
76    }
77
78    /* Getting the User's Locale
79     */
80
81    /// A locale which tracks the user’s current preferences.
82    #[property]
83    pub fn autoupdating_current_locale() -> NSLocale {
84        unsafe { NSLocale::from_id(msg_send![Self::m_class(), autoupdatingCurrentLocale]) }
85    }
86
87    /// A locale representing the user's region settings at the time the property is read.
88    #[property]
89    pub fn current_locale() -> NSLocale {
90        unsafe { NSLocale::from_id(msg_send![Self::m_class(), currentLocale]) }
91    }
92
93    /// A locale representing the generic root values with little localization.
94    #[property]
95    pub fn system_locale() -> NSLocale {
96        unsafe { NSLocale::from_id(msg_send![Self::m_class(), systemLocale]) }
97    }
98
99    /* Getting Known Identifiers and Codes
100     */
101
102    /// The list of locale identifiers available on the system.
103    #[property]
104    pub fn available_locale_identifiers() -> NSArray<NSString> {
105        unsafe { NSArray::from_id(msg_send![Self::m_class(), availableLocaleIdentifiers]) }
106    }
107
108    /// The list of known country or region codes.
109    #[property]
110    pub fn iso_country_codes() -> NSArray<NSString> {
111        unsafe { NSArray::from_id(msg_send![Self::m_class(), ISOCountryCodes]) }
112    }
113
114    /// The list of known language codes.
115    #[property]
116    pub fn iso_language_codes() -> NSArray<NSString> {
117        unsafe { NSArray::from_id(msg_send![Self::m_class(), ISOLanguageCodes]) }
118    }
119
120    /// The list of known currency codes.
121    #[property]
122    pub fn iso_currency_codes() -> NSArray<NSString> {
123        unsafe { NSArray::from_id(msg_send![Self::m_class(), ISOCurrencyCodes]) }
124    }
125
126    /// A list of commonly encountered currency codes.
127    #[property]
128    pub fn common_isocurrency_codes() -> NSArray<NSString> {
129        unsafe { NSArray::from_id(msg_send![Self::m_class(), commonISOCurrencyCodes]) }
130    }
131
132    /* Converting Between Identifiers
133     */
134
135    /// Returns the canonical identifier for a given locale identification string.
136    #[method]
137    pub fn canonical_locale_identifier_from_string(string: &NSString) -> NSString {
138        unsafe {
139            NSString::from_id(msg_send![
140                Self::m_class(),
141                canonicalLocaleIdentifierFromString: string.m_self()
142            ])
143        }
144    }
145
146    /// Returns a dictionary that is the result of parsing a locale ID.
147    #[method]
148    pub fn components_from_locale_identifier(
149        string: &NSString,
150    ) -> NSDictionary<NSString, NSString> {
151        unsafe {
152            NSDictionary::from_id(msg_send![
153                Self::m_class(),
154                componentsFromLocaleIdentifier: string.m_self()
155            ])
156        }
157    }
158
159    /// Returns a locale identifier from the components specified in a given dictionary.
160    #[method]
161    pub fn locale_identifier_from_components(dict: &NSDictionary<NSString, NSString>) -> NSString {
162        unsafe {
163            NSString::from_id(msg_send![
164                Self::m_class(),
165                localeIdentifierFromComponents: dict.m_self()
166            ])
167        }
168    }
169
170    /// Returns a canonical language identifier by mapping an arbitrary locale identification string to the canonical identifier.
171    #[method]
172    pub fn canonical_language_identifier_from_string(string: &NSString) -> NSString {
173        unsafe {
174            NSString::from_id(msg_send![
175                Self::m_class(),
176                canonicalLanguageIdentifierFromString: string.m_self()
177            ])
178        }
179    }
180
181    /// Returns a locale identifier from a Windows locale code.
182    #[method]
183    pub fn locale_identifier_from_windows_locale_code(lcid: u32) -> Option<NSString> {
184        unsafe {
185            let ptr = msg_send![Self::m_class(), localeIdentifierFromWindowsLocaleCode: lcid];
186
187            if ptr != nil {
188                Some(NSString::from_id(ptr))
189            } else {
190                None
191            }
192        }
193    }
194
195    /// Returns a Window locale code from the locale identifier.
196    #[method]
197    pub fn windows_locale_code_from_locale_identifier(locale_identifier: &NSString) -> u32 {
198        unsafe {
199            msg_send![
200                Self::m_class(),
201                windowsLocaleCodeFromLocaleIdentifier: locale_identifier.m_self()
202            ]
203        }
204    }
205
206    /* Getting Information About a Locale
207     */
208
209    /// The identifier for the locale.
210    #[property]
211    pub fn locale_identifier(&self) -> NSString {
212        unsafe { NSString::from_id(msg_send![self.m_self(), localeIdentifier]) }
213    }
214
215    /// The country or region code for the locale.
216    #[property]
217    pub fn country_code(&self) -> Option<NSString> {
218        unsafe {
219            let ptr = msg_send![self.m_self(), countryCode];
220
221            if ptr != nil {
222                Some(NSString::from_id(ptr))
223            } else {
224                None
225            }
226        }
227    }
228
229    /// The language code for the locale.
230    #[property]
231    pub fn language_code(&self) -> NSString {
232        unsafe { NSString::from_id(msg_send![self.m_self(), languageCode]) }
233    }
234
235    /// The script code for the locale.
236    #[property]
237    pub fn script_code(&self) -> Option<NSString> {
238        unsafe {
239            let ptr = msg_send![self.m_self(), scriptCode];
240
241            if ptr != nil {
242                Some(NSString::from_id(ptr))
243            } else {
244                None
245            }
246        }
247    }
248
249    /// The variant code for the locale.
250    #[property]
251    pub fn variant_code(&self) -> Option<NSString> {
252        unsafe {
253            let ptr = msg_send![self.m_self(), variantCode];
254
255            if ptr != nil {
256                Some(NSString::from_id(ptr))
257            } else {
258                None
259            }
260        }
261    }
262
263    /// The exemplar character set for the locale.
264    #[property]
265    pub fn exemplar_character_set(&self) -> NSCharacterSet {
266        unsafe { NSCharacterSet::from_id(msg_send![self.m_self(), exemplarCharacterSet]) }
267    }
268
269    /// The collation identifier for the locale.
270    #[property]
271    pub fn collation_identifier(&self) -> Option<NSString> {
272        unsafe {
273            let ptr = msg_send![self.m_self(), collationIdentifier];
274
275            if ptr != nil {
276                Some(NSString::from_id(ptr))
277            } else {
278                None
279            }
280        }
281    }
282
283    /// The collator identifier for the locale.
284    #[property]
285    pub fn collator_identifier(&self) -> NSString {
286        unsafe { NSString::from_id(msg_send![self.m_self(), collatorIdentifier]) }
287    }
288
289    /// A Boolean value that indicates whether the locale uses the metric system.
290    #[property]
291    pub fn uses_metric_system(&self) -> bool {
292        unsafe { to_bool(msg_send![self.m_self(), usesMetricSystem]) }
293    }
294
295    /// The decimal separator for the locale.
296    #[property]
297    pub fn decimal_separator(&self) -> NSString {
298        unsafe { NSString::from_id(msg_send![self.m_self(), decimalSeparator]) }
299    }
300
301    /// The grouping separator for the locale.
302    #[property]
303    pub fn grouping_separator(&self) -> NSString {
304        unsafe { NSString::from_id(msg_send![self.m_self(), groupingSeparator]) }
305    }
306
307    /// The currency code for the locale.
308    #[property]
309    pub fn currency_code(&self) -> Option<NSString> {
310        unsafe {
311            let ptr = msg_send![self.m_self(), currencyCode];
312
313            if ptr != nil {
314                Some(NSString::from_id(ptr))
315            } else {
316                None
317            }
318        }
319    }
320
321    /// The currency symbol for the locale.
322    #[property]
323    pub fn currency_symbol(&self) -> NSString {
324        unsafe { NSString::from_id(msg_send![self.m_self(), currencySymbol]) }
325    }
326
327    /// The calendar identifier for the locale.
328    #[property]
329    pub fn calendar_identifier(&self) -> NSString {
330        unsafe { NSString::from_id(msg_send![self.m_self(), calendarIdentifier]) }
331    }
332
333    /// The begin quotation symbol for the locale.
334    #[property]
335    pub fn quotation_begin_delimiter(&self) -> NSString {
336        unsafe { NSString::from_id(msg_send![self.m_self(), quotationBeginDelimiter]) }
337    }
338
339    /// The end quotation symbol for the locale.
340    #[property]
341    pub fn quotation_end_delimiter(&self) -> NSString {
342        unsafe { NSString::from_id(msg_send![self.m_self(), quotationEndDelimiter]) }
343    }
344
345    /// The alternate begin quotation symbol for the locale.
346    #[property]
347    pub fn alternate_quotation_begin_delimiter(&self) -> NSString {
348        unsafe { NSString::from_id(msg_send![self.m_self(), alternateQuotationBeginDelimiter]) }
349    }
350
351    /// The alternate end quotation symbol for the locale.
352    #[property]
353    pub fn alternate_quotation_end_delimiter(&self) -> NSString {
354        unsafe { NSString::from_id(msg_send![self.m_self(), alternateQuotationEndDelimiter]) }
355    }
356
357    /* Getting Display Information About a Locale
358     */
359
360    /// Returns the localized string for the specified locale identifier.
361    #[method]
362    pub fn localized_string_for_locale_identifier(&self, locale_identifier: &NSString) -> NSString {
363        unsafe {
364            NSString::from_id(msg_send![
365                self.m_self(),
366                localizedStringForLocaleIdentifier: locale_identifier.m_self()
367            ])
368        }
369    }
370
371    /// Returns the localized string for a country or region code.
372    #[method]
373    pub fn localized_string_for_country_code(&self, country_code: &NSString) -> Option<NSString> {
374        unsafe {
375            let ptr = msg_send![
376                self.m_self(),
377                localizedStringForCountryCode: country_code.m_self()
378            ];
379
380            if ptr != nil {
381                Some(NSString::from_id(ptr))
382            } else {
383                None
384            }
385        }
386    }
387
388    /// Returns the localized string for the specified language code.
389    #[method]
390    pub fn localized_string_for_language_code(&self, language_code: &NSString) -> Option<NSString> {
391        unsafe {
392            let ptr =
393                msg_send![self.m_self(), localizedStringForLanguageCode: language_code.m_self()];
394
395            if ptr != nil {
396                Some(NSString::from_id(ptr))
397            } else {
398                None
399            }
400        }
401    }
402
403    /// Returns the localized string for the specified script code.
404    #[method]
405    pub fn localized_string_for_script_code(&self, script_code: &NSString) -> Option<NSString> {
406        unsafe {
407            let ptr = msg_send![self.m_self(), localizedStringForScriptCode: script_code.m_self()];
408
409            if ptr != nil {
410                Some(NSString::from_id(ptr))
411            } else {
412                None
413            }
414        }
415    }
416    /// Returns the localized string for the specified variant code.
417    #[method]
418    pub fn localized_string_for_variant_code(&self, variant_code: &NSString) -> Option<NSString> {
419        unsafe {
420            let ptr =
421                msg_send![self.m_self(), localizedStringForVariantCode: variant_code.m_self()];
422
423            if ptr != nil {
424                Some(NSString::from_id(ptr))
425            } else {
426                None
427            }
428        }
429    }
430    /// Returns the localized string for the specified collation identifier.
431    #[method]
432    pub fn localized_string_for_collation_identifier(
433        &self,
434        collation_identifier: &NSString,
435    ) -> Option<NSString> {
436        unsafe {
437            let ptr = msg_send![
438                self.m_self(),
439                localizedStringForCollationIdentifier: collation_identifier.m_self()
440            ];
441
442            if ptr != nil {
443                Some(NSString::from_id(ptr))
444            } else {
445                None
446            }
447        }
448    }
449    /// Returns the localized string for the specified collator identifier.
450    #[method]
451    pub fn localized_string_for_collator_identifier(
452        &self,
453        collator_identifier: &NSString,
454    ) -> Option<NSString> {
455        unsafe {
456            let ptr = msg_send![
457                self.m_self(),
458                localizedStringForCollatorIdentifier: collator_identifier
459            ];
460
461            if ptr != nil {
462                Some(NSString::from_id(ptr))
463            } else {
464                None
465            }
466        }
467    }
468    /// Returns the localized string for the specified currency code.
469    #[method]
470    pub fn localized_string_for_currency_code(&self, currency_code: &NSString) -> Option<NSString> {
471        unsafe {
472            let ptr =
473                msg_send![self.m_self(), localizedStringForCurrencyCode: currency_code.m_self()];
474
475            if ptr != nil {
476                Some(NSString::from_id(ptr))
477            } else {
478                None
479            }
480        }
481    }
482    /// Returns the localized string for the specified language code.
483    #[method]
484    pub fn localized_string_for_calendar_identifier(
485        &self,
486        calendar_identifier: &NSString,
487    ) -> Option<NSString> {
488        unsafe {
489            let ptr = msg_send![
490                self.m_self(),
491                localizedStringForCalendarIdentifier: calendar_identifier.m_self()
492            ];
493
494            if ptr != nil {
495                Some(NSString::from_id(ptr))
496            } else {
497                None
498            }
499        }
500    }
501    /* Accessing Locale Information by Key
502     */
503
504    /// Returns the value of the component corresponding to the specified key.
505    #[method]
506    pub fn object_for_key(&self, key: NSLocaleKey) -> Option<id> {
507        unsafe {
508            let obj: id = msg_send![self.m_self(), objectForKey: key];
509            if obj == nil {
510                None
511            } else {
512                Some(obj)
513            }
514        }
515    }
516
517    /// Returns the display name for the given locale component value.
518    #[method]
519    pub fn display_name_for_key_value(
520        &self,
521        key: NSLocaleKey,
522        value: &NSString,
523    ) -> Option<NSString> {
524        unsafe {
525            let obj: id = msg_send![self.m_self(), displayNameForKey: key value: value.m_self()];
526            if obj == nil {
527                None
528            } else {
529                Some(NSString::from_id(obj))
530            }
531        }
532    }
533
534    /* Getting the User's Preferred Languages
535     */
536
537    /// An ordered list of the user's preferred languages.
538    #[property]
539    pub fn preferred_languages() -> NSArray<NSString> {
540        unsafe { NSArray::from_id(msg_send![Self::m_class(), preferredLanguages]) }
541    }
542
543    /* Getting Line and Character Direction for a Language
544     */
545
546    /// Returns the direction of the sequence of characters in a line for the specified ISO language code.
547    #[method]
548    pub fn character_direction_for_language(iso_language_code: NSString) -> LanguageDirection {
549        unsafe {
550            msg_send![
551                Self::m_class(),
552                characterDirectionForLanguage: iso_language_code
553            ]
554        }
555    }
556
557    /// Returns the direction of the sequence of lines for the specified ISO language code.
558    #[method]
559    pub fn line_direction_for_language(iso_language_code: NSString) -> LanguageDirection {
560        unsafe { msg_send![Self::m_class(), lineDirectionForLanguage: iso_language_code] }
561    }
562}
563
564extern "C" {
565    /// A notification that indicates that the user’s locale changed.
566    pub static NSCurrentLocaleDidChangeNotification: NSNotificationName;
567}
568
569#[cfg(test)]
570mod tests {
571    use super::NSLocale;
572
573    use crate::{
574        foundation::NSString,
575        objective_c_runtime::{nil, traits::PNSObject},
576    };
577
578    #[test]
579    fn test_current_locale() {
580        let locale = NSLocale::current_locale();
581        assert!(locale.m_self() != nil)
582    }
583
584    #[test]
585    fn test_init_with_locale_identifier() {
586        let ident = NSLocale::current_locale().locale_identifier();
587
588        assert!(
589            NSLocale::m_alloc()
590                .init_with_locale_identifier(&ident)
591                .locale_identifier()
592                == NSLocale::current_locale().locale_identifier()
593        );
594
595        assert!(
596            NSLocale::m_alloc()
597                .init_with_locale_identifier(&ident)
598                .locale_identifier()
599                == NSLocale::current_locale().locale_identifier()
600        );
601
602        assert!(
603            NSLocale::m_alloc()
604                .init_with_locale_identifier(&ident)
605                .locale_identifier()
606                == NSLocale::current_locale().locale_identifier()
607        );
608    }
609
610    #[test]
611    fn test_locale_with_locale_identifier() {
612        let ident = NSLocale::current_locale().locale_identifier();
613
614        assert!(
615            NSLocale::locale_with_locale_identifier(&ident).locale_identifier()
616                == NSLocale::current_locale().locale_identifier()
617        );
618
619        assert!(
620            NSLocale::locale_with_locale_identifier(&ident).locale_identifier()
621                == NSLocale::current_locale().locale_identifier()
622        );
623
624        assert!(
625            NSLocale::locale_with_locale_identifier(&ident).locale_identifier()
626                == NSLocale::current_locale().locale_identifier()
627        );
628    }
629
630    #[test]
631    fn test_country_less_locale() {
632        let name: NSString = "zh-Hans".into();
633        assert!(NSLocale::locale_with_locale_identifier(&name)
634            .country_code()
635            .is_none());
636    }
637
638    #[test]
639    fn test_properties() {
640        let en = NSLocale::locale_with_locale_identifier(&"en-US".into());
641        assert_eq!(en.alternate_quotation_begin_delimiter(), "‘");
642        assert_eq!(en.alternate_quotation_end_delimiter(), "’");
643        assert!(en.collation_identifier().is_none());
644        assert_eq!(en.collator_identifier(), "en-US");
645        assert_eq!(en.country_code(), Some("US".into()));
646        assert_eq!(en.currency_code(), Some("USD".into()));
647        assert_eq!(en.currency_symbol(), "$");
648        assert_eq!(en.decimal_separator(), ".");
649        assert_eq!(en.grouping_separator(), ",");
650        assert_eq!(en.language_code(), "en");
651        assert_eq!(en.locale_identifier(), "en-US");
652        assert_eq!(en.quotation_begin_delimiter(), "“");
653        assert_eq!(en.quotation_end_delimiter(), "”");
654    }
655}