@@ -22,21 +22,40 @@ pub struct Message {
22
22
pub attributes : HashMap < String , String > ,
23
23
}
24
24
25
- /// `FluentBundle` is a collection of localization messages which are meant to be used together
26
- /// in a single view, widget or any other UI abstraction.
25
+ /// A collection of localization messages for a single locale, which are meant
26
+ /// to be used together in a single view, widget or any other UI abstraction.
27
27
///
28
- /// # `FluentBundle` Life-cycle
28
+ /// # Examples
29
29
///
30
- /// To create a bundle, call `FluentBundle::new` with a locale list that represents the best
31
- /// possible fallback chain for a given locale. The simplest case is a one-locale list.
30
+ /// ```
31
+ /// use fluent::bundle::FluentBundle;
32
+ /// use fluent::types::FluentValue;
33
+ /// use std::collections::HashMap;
32
34
///
33
- /// Next, call `add_messages` one or more times, supplying translations in the FTL syntax. The
35
+ /// let mut bundle = FluentBundle::new(&["en-US"]);
36
+ /// bundle.add_messages("intro = Welcome, { $name }.");
37
+ ///
38
+ /// let mut args = HashMap::new();
39
+ /// args.insert("name", FluentValue::from("John"));
40
+ ///
41
+ /// let value = bundle.format("intro", Some(&args));
42
+ /// assert_eq!(value, Some(("Welcome, John.".to_string(), vec![])));
43
+ ///
44
+ /// ```
45
+ ///
46
+ /// # `FluentBundle` Life Cycle
47
+ ///
48
+ /// To create a bundle, call [`FluentBundle::new`] with a locale list that represents the best
49
+ /// possible fallback chain for a given locale. The simplest case is a one-locale list.
50
+ ///
51
+ /// Next, call [`add_messages`] one or more times, supplying translations in the FTL syntax. The
34
52
/// `FluentBundle` instance is now ready to be used for localization.
35
53
///
36
- /// To format a translation, call `get_message` to retrieve a `fluent::bundle::Message` structure
37
- /// and then `format` it within the bundle .
54
+ /// To format a translation, call [`format`] with a message id or path (a path is a message id
55
+ /// plus an attribute, e.g. 'hello-world.tooltip') .
38
56
///
39
- /// The result is an Option wrapping a single string that should be displayed in the UI. It is
57
+ /// The result is an [`Option<T>`] wrapping a `(String, Vec<FluentError>)`. On success, the string
58
+ /// is a formatted value that should be displayed in the UI. It is
40
59
/// recommended to treat the result as opaque from the perspective of the program and use it only
41
60
/// to display localized messages. Do not examine it or alter in any way before displaying. This
42
61
/// is a general good practice as far as all internationalization operations are concerned.
@@ -48,6 +67,13 @@ pub struct Message {
48
67
/// purpose of language negotiation with i18n formatters. For instance, if date and time formatting
49
68
/// are not available in the first locale, `FluentBundle` will use its `locales` fallback chain
50
69
/// to negotiate a sensible fallback for date and time formatting.
70
+ ///
71
+ /// [`add_messages`]: ./struct.FluentBundle.html#method.add_messages
72
+ /// [`FluentBundle::new`]: ./struct.FluentBundle.html#method.new
73
+ /// [`fluent::bundle::Message`]: ./struct.FluentBundle.html#method.new
74
+ /// [`format`]: ./struct.FluentBundle.html#method.format
75
+ /// [`get_message`]: ./struct.FluentBundle.html#method.get_message
76
+ /// [`Option<T>`]: http://doc.rust-lang.org/std/option/enum.Option.html
51
77
#[ allow( dead_code) ]
52
78
pub struct FluentBundle < ' bundle > {
53
79
pub locales : Vec < String > ,
@@ -56,6 +82,21 @@ pub struct FluentBundle<'bundle> {
56
82
}
57
83
58
84
impl < ' bundle > FluentBundle < ' bundle > {
85
+ /// Constructs a FluentBundle. `locales` is the fallback chain of locales
86
+ /// to use for formatters like date and time. `locales` does not influence
87
+ /// message selection.
88
+ ///
89
+ /// # Examples
90
+ ///
91
+ /// ```
92
+ /// use fluent::bundle::FluentBundle;
93
+ ///
94
+ /// let mut bundle = FluentBundle::new(&["en-US"]);
95
+ /// ```
96
+ ///
97
+ /// # Errors
98
+ ///
99
+ /// This will panic if no formatters can be found for the locales.
59
100
pub fn new < ' a , S : ToString > ( locales : & ' a [ S ] ) -> FluentBundle < ' bundle > {
60
101
let locales = locales
61
102
. into_iter ( )
@@ -77,10 +118,48 @@ impl<'bundle> FluentBundle<'bundle> {
77
118
}
78
119
}
79
120
121
+ /// Returns true if this bundle contains a message with the given id.
122
+ ///
123
+ /// # Examples
124
+ ///
125
+ /// ```
126
+ /// use fluent::bundle::FluentBundle;
127
+ ///
128
+ /// let mut bundle = FluentBundle::new(&["en-US"]);
129
+ /// bundle.add_messages("hello = Hi!");
130
+ /// assert_eq!(true, bundle.has_message("hello"));
131
+ /// ```
80
132
pub fn has_message ( & self , id : & str ) -> bool {
81
133
self . entries . get_message ( id) . is_some ( )
82
134
}
83
135
136
+ /// Makes the provided rust function available to messages with the name `id`. See
137
+ /// the [FTL syntax guide] to learn how these are used in messages.
138
+ ///
139
+ /// FTL functions accept both positional and named args. The rust function you
140
+ /// provide therefore has two parameters: a slice of values for the positional
141
+ /// args, and a HashMap of values for named args.
142
+ ///
143
+ /// # Examples
144
+ ///
145
+ /// ```
146
+ /// use fluent::bundle::FluentBundle;
147
+ /// use fluent::types::FluentValue;
148
+ ///
149
+ /// let mut bundle = FluentBundle::new(&["en-US"]);
150
+ ///
151
+ /// // Register a fn that maps from string to string length
152
+ /// bundle.add_function("STRLEN", |positional, named| match positional {
153
+ /// [Some(FluentValue::String(str))] => Some(FluentValue::Number(str.len().to_string())),
154
+ /// _ => None,
155
+ /// }).unwrap();
156
+ ///
157
+ /// bundle.add_messages("length = { STRLEN(\"12345\") }").unwrap();
158
+ /// let (value, _) = bundle.format("length", None).unwrap();
159
+ /// assert_eq!(&value, "5");
160
+ /// ```
161
+ ///
162
+ /// [FTL syntax guide]: https://projectfluent.org/fluent/guide/functions.html
84
163
pub fn add_function < F > ( & mut self , id : & str , func : F ) -> Result < ( ) , FluentError >
85
164
where
86
165
F : ' bundle
@@ -100,6 +179,33 @@ impl<'bundle> FluentBundle<'bundle> {
100
179
}
101
180
}
102
181
182
+ /// Adds the message or messages, in [FTL syntax], to the bundle, returning an
183
+ /// empty [`Result<T>`] on success.
184
+ ///
185
+ /// # Examples
186
+ ///
187
+ /// ```
188
+ /// use fluent::bundle::FluentBundle;
189
+ ///
190
+ /// let mut bundle = FluentBundle::new(&["en-US"]);
191
+ /// bundle.add_messages("
192
+ /// hello = Hi!
193
+ /// goodbye = Bye!
194
+ /// ");
195
+ /// assert_eq!(true, bundle.has_message("hello"));
196
+ /// ```
197
+ ///
198
+ /// # Whitespace
199
+ ///
200
+ /// Message ids must have no leading whitespace. Message values that span
201
+ /// multiple lines must have leading whitespace on all but the first line. These
202
+ /// are standard FTL syntax rules that may prove a bit troublesome in source
203
+ /// code formatting. The [`indoc!`] crate can help with stripping extra indentation
204
+ /// if you wish to indent your entire message.
205
+ ///
206
+ /// [FTL syntax]: https://projectfluent.org/fluent/guide/
207
+ /// [`indoc!`]: https://github.com/dtolnay/indoc
208
+ /// [`Result<T>`]: https://doc.rust-lang.org/std/result/enum.Result.html
103
209
pub fn add_messages ( & mut self , source : & str ) -> Result < ( ) , Vec < FluentError > > {
104
210
match FluentResource :: from_string ( source) {
105
211
Ok ( res) => self . add_resource ( res) ,
@@ -117,6 +223,7 @@ impl<'bundle> FluentBundle<'bundle> {
117
223
}
118
224
}
119
225
226
+ /// Use [`add_messages`](./struct.FluentBundle.html#method.add_messages) instead.
120
227
pub fn add_resource ( & mut self , res : FluentResource ) -> Result < ( ) , Vec < FluentError > > {
121
228
let mut errors = vec ! [ ] ;
122
229
@@ -150,6 +257,48 @@ impl<'bundle> FluentBundle<'bundle> {
150
257
}
151
258
}
152
259
260
+ /// Formats the message value identified by `path` using `args`.
261
+ /// `path` is either a message id ("hello"), or message id plus
262
+ /// attribute ("hello.tooltip").
263
+ ///
264
+ /// # Examples
265
+ ///
266
+ /// ```
267
+ /// use fluent::bundle::FluentBundle;
268
+ /// use fluent::types::FluentValue;
269
+ /// use std::collections::HashMap;
270
+ ///
271
+ /// let mut bundle = FluentBundle::new(&["en-US"]);
272
+ /// bundle.add_messages("intro = Welcome, { $name }.");
273
+ ///
274
+ /// let mut args = HashMap::new();
275
+ /// args.insert("name", FluentValue::from("John"));
276
+ ///
277
+ /// let value = bundle.format("intro", Some(&args));
278
+ /// assert_eq!(value, Some(("Welcome, John.".to_string(), vec![])));
279
+ ///
280
+ /// ```
281
+ ///
282
+ /// An example with attributes and no args:
283
+ ///
284
+ /// ```
285
+ /// use fluent::bundle::FluentBundle;
286
+ ///
287
+ /// let mut bundle = FluentBundle::new(&["en-US"]);
288
+ /// bundle.add_messages("
289
+ /// hello =
290
+ /// .title = Hi!
291
+ /// .tooltip = This says 'Hi!'
292
+ /// ");
293
+ ///
294
+ /// let value = bundle.format("hello.title", None);
295
+ /// assert_eq!(value, Some(("Hi!".to_string(), vec![])));
296
+ /// ```
297
+ ///
298
+ /// # Errors
299
+ ///
300
+ /// On error, the string returned will be the path. This allows
301
+ /// for slightly graceful fallback during errors.
153
302
pub fn format (
154
303
& self ,
155
304
path : & str ,
@@ -203,6 +352,7 @@ impl<'bundle> FluentBundle<'bundle> {
203
352
None
204
353
}
205
354
355
+ /// Use [`format`](./struct.FluentBundle.html#method.format) instead.
206
356
pub fn format_message (
207
357
& self ,
208
358
message_id : & str ,
0 commit comments