Skip to main content

es_fluent/traits/
label.rs

1use super::FluentLocalizer;
2use crate::registry::StaticFluentMessageKey;
3
4/// A trait for types that have a Fluent label key representing the type itself.
5///
6/// This trait is automatically implemented by `#[derive(EsFluentLabel)]` for
7/// source types, and by `#[derive(EsFluentVariants)]` for each generated
8/// variant enum.
9pub trait FluentLabel {
10    /// Returns the fully scoped static key for this type-level label.
11    fn fluent_label_key() -> StaticFluentMessageKey;
12
13    /// Attempts to return the localized label for this type using an explicit
14    /// localization context.
15    fn try_localize_label<L: FluentLocalizer + ?Sized>(localizer: &L) -> Option<String> {
16        try_localize_label(localizer, Self::fluent_label_key())
17    }
18
19    /// Returns the localized label for this type using an explicit localization
20    /// context.
21    ///
22    /// A label key generated under the package-local `fallback-str` policy
23    /// returns the labeled type's snake_case Rust name.
24    fn localize_label<L: FluentLocalizer + ?Sized>(localizer: &L) -> String {
25        localize_label(localizer, Self::fluent_label_key())
26    }
27}
28
29#[doc(hidden)]
30pub fn try_localize_label<L: FluentLocalizer + ?Sized>(
31    localizer: &L,
32    key: StaticFluentMessageKey,
33) -> Option<String> {
34    localizer.localize(key, None)
35}
36
37#[doc(hidden)]
38pub fn localize_label<L: FluentLocalizer + ?Sized>(
39    localizer: &L,
40    key: StaticFluentMessageKey,
41) -> String {
42    localizer
43        .localize(key, None)
44        .unwrap_or_else(|| super::missing_fluent_value(key, "label"))
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50    use crate::FluentArgs;
51
52    fn static_key(id: &'static str) -> StaticFluentMessageKey {
53        crate::registry::__macro::static_message_key(
54            "label-owner",
55            crate::registry::__macro::static_domain("label-domain"),
56            crate::registry::__macro::static_entry_id(id),
57        )
58    }
59
60    fn fallback_key(id: &'static str, fallback: &'static str) -> StaticFluentMessageKey {
61        crate::registry::__macro::static_message_key_with_fallback(
62            "label-owner",
63            crate::registry::__macro::static_domain("label-domain"),
64            crate::registry::__macro::static_entry_id(id),
65            fallback,
66        )
67    }
68
69    struct LabelLocalizer;
70
71    impl FluentLocalizer for LabelLocalizer {
72        fn localize<'a>(
73            &self,
74            key: StaticFluentMessageKey,
75            _args: Option<&FluentArgs<'a>>,
76        ) -> Option<String> {
77            (key.owner() == "label-owner"
78                && key.domain() == "label-domain"
79                && key.id() == "label-id")
80                .then(|| "Label".to_string())
81        }
82    }
83
84    struct TestLabel;
85
86    impl FluentLabel for TestLabel {
87        fn fluent_label_key() -> StaticFluentMessageKey {
88            static_key("label-id")
89        }
90    }
91
92    struct FallbackLabel;
93
94    impl FluentLabel for FallbackLabel {
95        fn fluent_label_key() -> StaticFluentMessageKey {
96            fallback_key("missing-id", "fallback_label")
97        }
98    }
99
100    #[test]
101    fn label_trait_exposes_typed_metadata_and_localizes() {
102        let localizer = LabelLocalizer;
103
104        assert_eq!(TestLabel::fluent_label_key().owner(), "label-owner");
105        assert_eq!(TestLabel::fluent_label_key().domain(), "label-domain");
106        assert_eq!(TestLabel::fluent_label_key().id(), "label-id");
107        assert_eq!(
108            TestLabel::try_localize_label(&localizer),
109            Some("Label".into())
110        );
111        assert_eq!(TestLabel::localize_label(&localizer), "Label");
112        assert_eq!(FallbackLabel::localize_label(&localizer), "fallback_label");
113        assert_eq!(FallbackLabel::try_localize_label(&localizer), None);
114    }
115
116    #[test]
117    fn localize_label_helpers_return_localized_values_or_explicitly_report_missing_values() {
118        let localizer = LabelLocalizer;
119
120        assert_eq!(
121            try_localize_label(&localizer, static_key("label-id")),
122            Some("Label".into())
123        );
124        assert_eq!(
125            try_localize_label(&localizer, static_key("missing-id")),
126            None
127        );
128        assert_eq!(localize_label(&localizer, static_key("label-id")), "Label");
129    }
130
131    #[test]
132    #[should_panic(
133        expected = "missing Fluent label `missing-id` in domain `label-domain` owned by `label-owner`"
134    )]
135    fn localize_label_panics_when_the_typed_label_is_missing() {
136        let _ = localize_label(&LabelLocalizer, static_key("missing-id"));
137    }
138}