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}
275