icu_experimental/personnames/specifications/
derive_name_order.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// This file is part of ICU4X. For terms of use, please see the file
// called LICENSE at the top level of the ICU4X source tree
// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).

use icu_locale::fallback::LocaleFallbackerBorrowed;
use icu_locale_core::subtags::Language;
use icu_locale_core::Locale;
use writeable::Writeable;
use zerovec::VarZeroVec;

use crate::personnames::api::FormattingOrder;

///
/// https://www.unicode.org/reports/tr35/tr35-personNames.html#derive-the-name-order
pub fn name_order_derive(
    person_name_locale: &Locale,
    surname_first: &VarZeroVec<str>,
    given_first: &VarZeroVec<str>,
    fallbacker: LocaleFallbackerBorrowed,
) -> FormattingOrder {
    // Create a LocaleFallbackerIterator with a default configuration.
    // By default, uses language priority with no additional extension keywords.
    let mut fallback_iterator = fallbacker
        .for_config(Default::default())
        .fallback_for(person_name_locale.into());

    loop {
        let chain_locale = fallback_iterator.get();
        let chain_locale_str = chain_locale.write_to_string();

        // switch lookup with UND
        let mut chain_locale_und = chain_locale.clone();
        chain_locale_und.language = Language::UND;
        let chain_locale_und_str = chain_locale_und.write_to_string();

        if given_first
            .iter()
            .any(|i| i == chain_locale_str || i == chain_locale_und_str)
        {
            return FormattingOrder::GivenFirst;
        }
        if surname_first
            .iter()
            .any(|i| i == chain_locale_str || i == chain_locale_und_str)
        {
            return FormattingOrder::SurnameFirst;
        }
        fallback_iterator.step();
    }
}

#[cfg(test)]
mod tests {
    use icu_locale::LocaleFallbacker;
    use icu_locale_core::locale;
    use zerovec::VarZeroVec;

    use super::name_order_derive;
    use crate::personnames::api::FormattingOrder;

    #[test]
    fn test_plain_locale() {
        let fallbacker = LocaleFallbacker::new();

        let given_first = VarZeroVec::from(&["und"]);
        // will never match by definition because there only have locale, but given first has precedence.
        let surname_first = VarZeroVec::from(&["hu", "ja", "km", "ko", "mn", "vi", "yue", "zh"]);

        // Match "und"
        assert_eq!(
            name_order_derive(
                &locale!("de_Latn_ch"),
                &surname_first,
                &given_first,
                fallbacker
            ),
            FormattingOrder::GivenFirst,
            "failed for de_Latn_ch"
        );

        // As weird as it may look, it returns GivenFirst because given_first has precedence
        // over surname_first in evaluation.
        // As evaluation goes, the locale being checked are {"ja", "und"},
        // since "und" is a catch all set in given first, it is a perfect match.
        assert_eq!(
            name_order_derive(
                &locale!("ja_Jpan_jp"),
                &surname_first,
                &given_first,
                fallbacker
            ),
            FormattingOrder::GivenFirst,
            "failed for ja_Jpan_jp"
        );
    }
    #[test]
    fn test_mixed_und() {
        let fallbacker = LocaleFallbacker::new();

        let given_first = VarZeroVec::from(&["und"]);
        let surname_first = VarZeroVec::from(&[
            "zh", "ja", "und-CN", "und-TW", "und-SG", "und-HK", "und-MO", "und-HU", "und-JP",
        ]);

        assert_eq!(
            name_order_derive(
                &locale!("en_Latn_SG"),
                &surname_first,
                &given_first,
                fallbacker
            ),
            FormattingOrder::SurnameFirst,
            "failed for en_Latn_SG"
        );

        // This is not matching because of zh, but because of und-CN
        assert_eq!(
            name_order_derive(
                &locale!("zh_Hans_CN"),
                &surname_first,
                &given_first,
                fallbacker
            ),
            FormattingOrder::SurnameFirst,
            "failed for zh_Hans_CN"
        );

        // This is not matching because of zh, but because of und-CN
        assert_eq!(
            name_order_derive(
                &locale!("zh_Hans"),
                &surname_first,
                &given_first,
                fallbacker
            ),
            FormattingOrder::GivenFirst,
            "failed for zh_Hans"
        );
    }
}