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