fluent_bundle/
message.rs

1use fluent_syntax::ast;
2
3/// [`FluentAttribute`] is a component of a compound [`FluentMessage`].
4///
5/// It represents a key-value pair providing a translation of a component
6/// of a user interface widget localized by the given message.
7///
8/// # Example
9///
10/// ```
11/// use fluent_bundle::{FluentResource, FluentBundle};
12///
13/// let source = r#"
14///
15/// confirm-modal = Are you sure?
16///     .confirm = Yes
17///     .cancel = No
18///     .tooltip = Closing the window will lose all unsaved data.
19///
20/// "#;
21///
22/// let resource = FluentResource::try_new(source.to_string())
23///     .expect("Failed to parse the resource.");
24///
25/// let mut bundle = FluentBundle::default();
26/// bundle.add_resource(resource)
27///     .expect("Failed to add a resource.");
28///
29/// let msg = bundle.get_message("confirm-modal")
30///     .expect("Failed to retrieve a message.");
31///
32/// let mut err = vec![];
33///
34/// let attributes = msg.attributes().map(|attr| {
35///     bundle.format_pattern(attr.value(), None, &mut err)
36/// }).collect::<Vec<_>>();
37///
38/// assert_eq!(attributes[0], "Yes");
39/// assert_eq!(attributes[1], "No");
40/// assert_eq!(attributes[2], "Closing the window will lose all unsaved data.");
41/// ```
42#[derive(Debug, PartialEq)]
43pub struct FluentAttribute<'m> {
44    node: &'m ast::Attribute<&'m str>,
45}
46
47impl<'m> FluentAttribute<'m> {
48    /// Retrieves an id of an attribute.
49    ///
50    /// # Example
51    ///
52    /// ```
53    /// # use fluent_bundle::{FluentResource, FluentBundle};
54    /// # let source = r#"
55    /// # confirm-modal =
56    /// #     .confirm = Yes
57    /// # "#;
58    /// # let resource = FluentResource::try_new(source.to_string())
59    /// #     .expect("Failed to parse the resource.");
60    /// # let mut bundle = FluentBundle::default();
61    /// # bundle.add_resource(resource)
62    /// #     .expect("Failed to add a resource.");
63    /// let msg = bundle.get_message("confirm-modal")
64    ///     .expect("Failed to retrieve a message.");
65    ///
66    /// let attr1 = msg.attributes().next()
67    ///     .expect("Failed to retrieve an attribute.");
68    ///
69    /// assert_eq!(attr1.id(), "confirm");
70    /// ```
71    pub fn id(&self) -> &'m str {
72        self.node.id.name
73    }
74
75    /// Retrieves an value of an attribute.
76    ///
77    /// # Example
78    ///
79    /// ```
80    /// # use fluent_bundle::{FluentResource, FluentBundle};
81    /// # let source = r#"
82    /// # confirm-modal =
83    /// #     .confirm = Yes
84    /// # "#;
85    /// # let resource = FluentResource::try_new(source.to_string())
86    /// #     .expect("Failed to parse the resource.");
87    /// # let mut bundle = FluentBundle::default();
88    /// # bundle.add_resource(resource)
89    /// #     .expect("Failed to add a resource.");
90    /// let msg = bundle.get_message("confirm-modal")
91    ///     .expect("Failed to retrieve a message.");
92    ///
93    /// let attr1 = msg.attributes().next()
94    ///     .expect("Failed to retrieve an attribute.");
95    ///
96    /// let mut err = vec![];
97    ///
98    /// let value = attr1.value();
99    /// assert_eq!(
100    ///     bundle.format_pattern(value, None, &mut err),
101    ///     "Yes"
102    /// );
103    /// ```
104    pub fn value(&self) -> &'m ast::Pattern<&'m str> {
105        &self.node.value
106    }
107}
108
109impl<'m> From<&'m ast::Attribute<&'m str>> for FluentAttribute<'m> {
110    fn from(attr: &'m ast::Attribute<&'m str>) -> Self {
111        FluentAttribute { node: attr }
112    }
113}
114
115/// [`FluentMessage`] is a basic translation unit of the Fluent system.
116///
117/// The instance of a message is returned from the
118/// [`FluentBundle::get_message`](crate::bundle::FluentBundle::get_message)
119/// method, for the lifetime of the [`FluentBundle`](crate::bundle::FluentBundle) instance.
120///
121/// # Example
122///
123/// ```
124/// use fluent_bundle::{FluentResource, FluentBundle};
125///
126/// let source = r#"
127///
128/// hello-world = Hello World!
129///
130/// "#;
131///
132/// let resource = FluentResource::try_new(source.to_string())
133///     .expect("Failed to parse the resource.");
134///
135/// let mut bundle = FluentBundle::default();
136/// bundle.add_resource(resource)
137///     .expect("Failed to add a resource.");
138///
139/// let msg = bundle.get_message("hello-world")
140///     .expect("Failed to retrieve a message.");
141///
142/// assert!(msg.value().is_some());
143/// ```
144///
145/// That value can be then passed to
146/// [`FluentBundle::format_pattern`](crate::bundle::FluentBundle::format_pattern) to be formatted
147/// within the context of a given [`FluentBundle`](crate::bundle::FluentBundle) instance.
148///
149/// # Compound Message
150///
151/// A message may contain a `value`, but it can also contain a list of [`FluentAttribute`] elements.
152///
153/// If a message contains attributes, it is called a "compound" message.
154///
155/// In such case, the message contains a list of key-value attributes that represent
156/// different translation values associated with a single translation unit.
157///
158/// This is useful for scenarios where a [`FluentMessage`] is associated with a
159/// complex User Interface widget which has multiple attributes that need to be translated.
160/// ```text
161/// confirm-modal = Are you sure?
162///     .confirm = Yes
163///     .cancel = No
164///     .tooltip = Closing the window will lose all unsaved data.
165/// ```
166#[derive(Debug, PartialEq)]
167pub struct FluentMessage<'m> {
168    node: &'m ast::Message<&'m str>,
169}
170
171impl<'m> FluentMessage<'m> {
172    /// Retrieves an option of a [`ast::Pattern`](fluent_syntax::ast::Pattern).
173    ///
174    /// # Example
175    ///
176    /// ```
177    /// # use fluent_bundle::{FluentResource, FluentBundle};
178    /// # let source = r#"
179    /// # hello-world = Hello World!
180    /// # "#;
181    /// # let resource = FluentResource::try_new(source.to_string())
182    /// #     .expect("Failed to parse the resource.");
183    /// # let mut bundle = FluentBundle::default();
184    /// # bundle.add_resource(resource)
185    /// #     .expect("Failed to add a resource.");
186    /// let msg = bundle.get_message("hello-world")
187    ///     .expect("Failed to retrieve a message.");
188    ///
189    /// if let Some(value) = msg.value() {
190    ///     let mut err = vec![];
191    ///     assert_eq!(
192    ///         bundle.format_pattern(value, None, &mut err),
193    ///         "Hello World!"
194    ///     );
195    /// #   assert_eq!(err.len(), 0);
196    /// }
197    /// ```
198    pub fn value(&self) -> Option<&'m ast::Pattern<&'m str>> {
199        self.node.value.as_ref()
200    }
201
202    /// An iterator over [`FluentAttribute`] elements.
203    ///
204    /// # Example
205    ///
206    /// ```
207    /// # use fluent_bundle::{FluentResource, FluentBundle};
208    /// # let source = r#"
209    /// # hello-world =
210    /// #     .label = This is a label
211    /// #     .accesskey = C
212    /// # "#;
213    /// # let resource = FluentResource::try_new(source.to_string())
214    /// #     .expect("Failed to parse the resource.");
215    /// # let mut bundle = FluentBundle::default();
216    /// # bundle.add_resource(resource)
217    /// #     .expect("Failed to add a resource.");
218    /// let msg = bundle.get_message("hello-world")
219    ///     .expect("Failed to retrieve a message.");
220    ///
221    /// let mut err = vec![];
222    ///
223    /// for attr in msg.attributes() {
224    ///     let _ = bundle.format_pattern(attr.value(), None, &mut err);
225    /// }
226    /// # assert_eq!(err.len(), 0);
227    /// ```
228    pub fn attributes(&self) -> impl Iterator<Item = FluentAttribute<'m>> {
229        self.node.attributes.iter().map(Into::into)
230    }
231
232    /// Retrieve a single [`FluentAttribute`] element.
233    ///
234    /// # Example
235    ///
236    /// ```
237    /// # use fluent_bundle::{FluentResource, FluentBundle};
238    /// # let source = r#"
239    /// # hello-world =
240    /// #     .label = This is a label
241    /// #     .accesskey = C
242    /// # "#;
243    /// # let resource = FluentResource::try_new(source.to_string())
244    /// #     .expect("Failed to parse the resource.");
245    /// # let mut bundle = FluentBundle::default();
246    /// # bundle.add_resource(resource)
247    /// #     .expect("Failed to add a resource.");
248    /// let msg = bundle.get_message("hello-world")
249    ///     .expect("Failed to retrieve a message.");
250    ///
251    /// let mut err = vec![];
252    ///
253    /// if let Some(attr) = msg.get_attribute("label") {
254    ///     assert_eq!(
255    ///         bundle.format_pattern(attr.value(), None, &mut err),
256    ///         "This is a label"
257    ///     );
258    /// }
259    /// # assert_eq!(err.len(), 0);
260    /// ```
261    pub fn get_attribute(&self, key: &str) -> Option<FluentAttribute<'m>> {
262        self.node
263            .attributes
264            .iter()
265            .find(|attr| attr.id.name == key)
266            .map(Into::into)
267    }
268}
269
270impl<'m> From<&'m ast::Message<&'m str>> for FluentMessage<'m> {
271    fn from(msg: &'m ast::Message<&'m str>) -> Self {
272        FluentMessage { node: msg }
273    }
274}