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}