Skip to content

Commit e77d4ba

Browse files
committed
Add docs for FluentBundle
Added docs for FluentBundle. Also, corrected some errors in the pre-existing docs.
1 parent b9a3371 commit e77d4ba

File tree

1 file changed

+159
-9
lines changed

1 file changed

+159
-9
lines changed

fluent/src/bundle.rs

Lines changed: 159 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,40 @@ pub struct Message {
2222
pub attributes: HashMap<String, String>,
2323
}
2424

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.
2727
///
28-
/// # `FluentBundle` Life-cycle
28+
/// # Examples
2929
///
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;
3234
///
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
3452
/// `FluentBundle` instance is now ready to be used for localization.
3553
///
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').
3856
///
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
4059
/// recommended to treat the result as opaque from the perspective of the program and use it only
4160
/// to display localized messages. Do not examine it or alter in any way before displaying. This
4261
/// is a general good practice as far as all internationalization operations are concerned.
@@ -48,6 +67,13 @@ pub struct Message {
4867
/// purpose of language negotiation with i18n formatters. For instance, if date and time formatting
4968
/// are not available in the first locale, `FluentBundle` will use its `locales` fallback chain
5069
/// 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
5177
#[allow(dead_code)]
5278
pub struct FluentBundle<'bundle> {
5379
pub locales: Vec<String>,
@@ -56,6 +82,21 @@ pub struct FluentBundle<'bundle> {
5682
}
5783

5884
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.
59100
pub fn new<'a, S: ToString>(locales: &'a [S]) -> FluentBundle<'bundle> {
60101
let locales = locales
61102
.into_iter()
@@ -77,10 +118,48 @@ impl<'bundle> FluentBundle<'bundle> {
77118
}
78119
}
79120

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+
/// ```
80132
pub fn has_message(&self, id: &str) -> bool {
81133
self.entries.get_message(id).is_some()
82134
}
83135

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
84163
pub fn add_function<F>(&mut self, id: &str, func: F) -> Result<(), FluentError>
85164
where
86165
F: 'bundle
@@ -100,6 +179,33 @@ impl<'bundle> FluentBundle<'bundle> {
100179
}
101180
}
102181

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
103209
pub fn add_messages(&mut self, source: &str) -> Result<(), Vec<FluentError>> {
104210
match FluentResource::from_string(source) {
105211
Ok(res) => self.add_resource(res),
@@ -117,6 +223,7 @@ impl<'bundle> FluentBundle<'bundle> {
117223
}
118224
}
119225

226+
/// Use [`add_messages`](./struct.FluentBundle.html#method.add_messages) instead.
120227
pub fn add_resource(&mut self, res: FluentResource) -> Result<(), Vec<FluentError>> {
121228
let mut errors = vec![];
122229

@@ -150,6 +257,48 @@ impl<'bundle> FluentBundle<'bundle> {
150257
}
151258
}
152259

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.
153302
pub fn format(
154303
&self,
155304
path: &str,
@@ -203,6 +352,7 @@ impl<'bundle> FluentBundle<'bundle> {
203352
None
204353
}
205354

355+
/// Use [`format`](./struct.FluentBundle.html#method.format) instead.
206356
pub fn format_message(
207357
&self,
208358
message_id: &str,

0 commit comments

Comments
 (0)