Skip to main content

cloudiful_bevy_localization/
lib.rs

1mod definition;
2mod definition_registry;
3mod error;
4mod loader;
5mod locale;
6mod localization;
7mod text_key;
8mod validation;
9
10pub use definition::{LocaleSource, LocalizationDefinition};
11pub use definition_registry::register_definition;
12pub use error::LocalizationLoadError;
13pub use locale::Locale;
14pub use localization::{Localization, LocalizationPlugin};
15pub use text_key::TextKey;
16
17pub fn locale_name_key_id(locale_id: &str) -> String {
18    format!(
19        "common.locale_name.{}",
20        locale_id.replace('-', "_").to_ascii_lowercase()
21    )
22}
23
24#[cfg(test)]
25mod tests {
26    use super::*;
27
28    const BASE_KEYS: &[TextKey] = &[
29        TextKey::new("common.greeting"),
30        TextKey::new("common.nested.label"),
31    ];
32    const BASE_SOURCES: &[LocaleSource] = &[
33        LocaleSource {
34            locale: "en-US",
35            namespace: "common",
36            contents: r#"
37greeting = "Hello {name}"
38[locale_name]
39en_us = "English"
40zh_cn = "Chinese"
41[nested]
42label = "Nested"
43"#,
44        },
45        LocaleSource {
46            locale: "zh-CN",
47            namespace: "common",
48            contents: r#"
49greeting = "你好 {name}"
50[locale_name]
51en_us = "英语"
52zh_cn = "中文"
53[nested]
54label = "嵌套"
55"#,
56        },
57    ];
58    static BASE_DEFINITION: LocalizationDefinition = LocalizationDefinition {
59        fallback_locale: "en-US",
60        locales: &["en-US", "zh-CN"],
61        sources: BASE_SOURCES,
62        keys: BASE_KEYS,
63    };
64
65    #[test]
66    fn locale_files_are_flattened() {
67        let localization =
68            Localization::from_definition(&BASE_DEFINITION).expect("base definition should load");
69
70        assert_eq!(
71            localization.lookup_id(Locale::new("en-US"), "common.nested.label"),
72            Some("Nested")
73        );
74    }
75
76    #[test]
77    fn missing_keys_fail_validation() {
78        const KEYS: &[TextKey] = &[TextKey::new("common.greeting"), TextKey::new("common.bye")];
79        static DEFINITION: LocalizationDefinition = LocalizationDefinition {
80            fallback_locale: "en-US",
81            locales: &["en-US"],
82            sources: &[LocaleSource {
83                locale: "en-US",
84                namespace: "common",
85                contents: r#"
86greeting = "Hello"
87[locale_name]
88en_us = "English"
89"#,
90            }],
91            keys: KEYS,
92        };
93
94        let err = Localization::from_definition(&DEFINITION).expect_err("missing key should fail");
95        assert!(
96            err.to_string()
97                .contains("missing localization key 'common.bye'")
98        );
99    }
100
101    #[test]
102    fn placeholder_mismatch_fails_validation() {
103        static DEFINITION: LocalizationDefinition = LocalizationDefinition {
104            fallback_locale: "en-US",
105            locales: &["en-US", "zh-CN"],
106            sources: &[
107                LocaleSource {
108                    locale: "en-US",
109                    namespace: "common",
110                    contents: r#"
111greeting = "Hello {name}"
112[locale_name]
113en_us = "English"
114zh_cn = "Chinese"
115[nested]
116label = "Nested"
117"#,
118                },
119                LocaleSource {
120                    locale: "zh-CN",
121                    namespace: "common",
122                    contents: r#"
123greeting = "你好 {user}"
124[locale_name]
125en_us = "英语"
126zh_cn = "中文"
127[nested]
128label = "嵌套"
129"#,
130                },
131            ],
132            keys: BASE_KEYS,
133        };
134
135        let err = Localization::from_definition(&DEFINITION)
136            .expect_err("placeholder mismatch should fail");
137        assert!(err.to_string().contains("placeholder mismatch"));
138    }
139
140    #[test]
141    fn text_lookup_falls_back_to_fallback_locale() {
142        let mut localization =
143            Localization::from_definition(&BASE_DEFINITION).expect("base definition should load");
144
145        localization.set_locale(Locale::new("fr-FR"));
146
147        assert_eq!(
148            localization.text(TextKey::new("common.greeting")),
149            "Hello {name}"
150        );
151        assert_eq!(
152            localization.format_text(TextKey::new("common.greeting"), [("name", "Alex")]),
153            "Hello Alex"
154        );
155    }
156}