1 | use 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)] |
43 | pub struct FluentAttribute<'m> { |
44 | node: &'m ast::Attribute<&'m str>, |
45 | } |
46 | |
47 | impl<'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 | |
109 | impl<'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)] |
167 | pub struct FluentMessage<'m> { |
168 | node: &'m ast::Message<&'m str>, |
169 | } |
170 | |
171 | impl<'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 | |
270 | impl<'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 | |