Skip to main content

es_fluent_manager_bevy/
components.rs

1use bevy::prelude::*;
2use es_fluent::FluentMessage;
3
4/// A Bevy component that holds localized text content.
5///
6/// `FluentText` is a generic component that wraps any type implementing
7/// `FluentMessage` and `Clone`. It's designed to work with the `es-fluent`
8/// localization system in Bevy applications.
9///
10/// When its message type is registered through `#[derive(BevyFluentText)]` or
11/// manual registration, `FluentText` updates Bevy `Text` components when the
12/// application locale changes.
13///
14/// # Examples
15///
16/// ```ignore
17/// use bevy::prelude::*;
18/// use es_fluent::EsFluent;
19/// use es_fluent_manager_bevy::{BevyFluentText, FluentText};
20///
21/// // The message key is derived from the struct name: "simple-message"
22/// #[derive(BevyFluentText, Clone, EsFluent)]
23/// struct SimpleMessage {
24///     name: String,
25/// }
26///
27/// fn setup_text(mut commands: Commands) {
28///     let content = SimpleMessage { name: "World".to_string() };
29///     commands.spawn((FluentText::new(content), Text::new("")));
30/// }
31/// # fn main() {}
32/// ```
33#[derive(Clone, Component)]
34pub struct FluentText<T: FluentMessage + Clone> {
35    /// The localized text content.
36    pub value: T,
37}
38
39impl<T: FluentMessage + Clone> FluentText<T> {
40    /// Creates a new `FluentText` component with the given value.
41    ///
42    /// # Arguments
43    ///
44    /// * `value` - The text content that implements `FluentMessage` and `Clone`
45    ///
46    /// # Examples
47    ///
48    /// Create a FluentText component with a simple string message:
49    ///
50    /// ```no_run
51    /// use es_fluent_manager_bevy::FluentText;
52    /// use es_fluent::EsFluent;
53    ///
54    /// // The message key is derived from the struct name: "message"
55    /// #[derive(Clone, EsFluent)]
56    /// struct Message {
57    ///     content: String,
58    /// }
59    ///
60    /// let text = FluentText::new(Message { content: "Hello".to_string() });
61    /// ```
62    pub fn new(value: T) -> Self {
63        Self { value }
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[derive(Clone)]
72    struct FakeMessage(&'static str);
73
74    impl FluentMessage for FakeMessage {
75        fn to_fluent_string_with(
76            &self,
77            _localize: &mut es_fluent::FluentMessageLookup<'_>,
78        ) -> String {
79            self.0.to_string()
80        }
81    }
82
83    #[test]
84    fn fluent_text_new_stores_inner_value() {
85        let component = FluentText::new(FakeMessage("hello"));
86        assert_eq!(component.value.0, "hello");
87    }
88
89    #[test]
90    fn fluent_text_clone_clones_inner_value() {
91        let component = FluentText::new(FakeMessage("hello"));
92        let cloned = component.clone();
93
94        assert_eq!(component.value.0, cloned.value.0);
95    }
96
97    #[test]
98    fn fluent_text_can_be_inserted_as_bevy_component() {
99        let mut world = World::new();
100        let entity = world.spawn(FluentText::new(FakeMessage("hello"))).id();
101
102        let component = world
103            .get::<FluentText<FakeMessage>>(entity)
104            .expect("component should be present");
105        assert_eq!(component.value.0, "hello");
106    }
107
108    #[test]
109    fn fluent_text_value_can_render_through_fluent_message_trait() {
110        let component = FluentText::new(FakeMessage("hello"));
111        let mut localize =
112            |_key: es_fluent::registry::StaticFluentMessageKey,
113             _args: Option<&es_fluent::FluentArgs<'_>>| { "unused".to_string() };
114
115        assert_eq!(
116            component.value.to_fluent_string_with(&mut localize),
117            "hello"
118        );
119    }
120}