Skip to content

Commit

Permalink
Improve docs
Browse files Browse the repository at this point in the history
  • Loading branch information
zbraniecki committed Jul 30, 2019
1 parent a78f45e commit 817c2cb
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 24 deletions.
60 changes: 55 additions & 5 deletions fluent-bundle/src/bundle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@ use crate::resolve::{ResolveValue, Scope};
use crate::resource::FluentResource;
use crate::types::FluentValue;

/// A single localization unit composed of an identifier,
/// value, and attributes.
#[derive(Debug, PartialEq)]
pub struct FluentMessage<'m> {
pub value: Option<&'m ast::Pattern<'m>>,
pub attributes: HashMap<&'m str, &'m ast::Pattern<'m>>,
}

/// A map of arguments passed from the code to
/// the localization to be used for message
/// formatting.
pub type FluentArgs<'args> = HashMap<&'args str, FluentValue<'args>>;

/// A collection of localization messages for a single locale, which are meant
Expand All @@ -46,6 +51,7 @@ pub type FluentArgs<'args> = HashMap<&'args str, FluentValue<'args>>;
///
/// let langid_en = langid!("en-US");
/// let mut bundle = FluentBundle::new(&[langid_en]);
///
/// bundle.add_resource(&resource)
/// .expect("Failed to add FTL resources to the bundle.");
///
Expand All @@ -62,36 +68,62 @@ pub type FluentArgs<'args> = HashMap<&'args str, FluentValue<'args>>;
///
/// # `FluentBundle` Life Cycle
///
/// ## Create a bundle
///
/// To create a bundle, call [`FluentBundle::new`] with a locale list that represents the best
/// possible fallback chain for a given locale. The simplest case is a one-locale list.
///
/// Next, call [`add_resource`] one or more times, supplying translations in the FTL syntax. The
/// `FluentBundle` instance is now ready to be used for localization.
/// Fluent uses [`LanguageIdentifier`] which can be created using `langid!` macro.
///
/// ## Add Resources
///
/// Next, call [`add_resource`] one or more times, supplying translations in the FTL syntax.
///
/// Since [`FluentBundle`] is generic over anything that can borrow a [`FluentResource`],
/// one can use [`FluentBundle`] to own its resources, store references to them,
/// or even [`Rc<FluentResource>`] or [`Arc<FluentResource>`].
///
/// The [`FluentBundle`] instance is now ready to be used for localization.
///
/// ## Format
///
/// To format a translation, call [`get_message`] to retrieve a [`FluentMessage`],
/// and then call [`format_pattern`] on the message value or attribute in order to
/// retrieve the translated string.
///
/// The result of `format_pattern` is an [`Cow<str>`]. It is
/// The result of [`format_pattern`] is an [`Cow<str>`]. It is
/// recommended to treat the result as opaque from the perspective of the program and use it only
/// to display localized messages. Do not examine it or alter in any way before displaying. This
/// is a general good practice as far as all internationalization operations are concerned.
///
/// If errors were encountered during formatting, they will be
/// accumulated in the [`Vec<FluentError>`] passed as the third argument.
///
/// While they are not fatal, they usually indicate problems with the translation,
/// and should be logged or reported in a way that allows the developer to notice
/// and fix them.
///
///
/// # Locale Fallback Chain
///
/// `FluentBundle` stores messages in a single locale, but keeps a locale fallback chain for the
/// [`FluentBundle`] stores messages in a single locale, but keeps a locale fallback chain for the
/// purpose of language negotiation with i18n formatters. For instance, if date and time formatting
/// are not available in the first locale, `FluentBundle` will use its `locales` fallback chain
/// are not available in the first locale, [`FluentBundle`] will use its `locales` fallback chain
/// to negotiate a sensible fallback for date and time formatting.
///
/// [`add_resource`]: ./struct.FluentBundle.html#method.add_resource
/// [`FluentBundle::new`]: ./struct.FluentBundle.html#method.new
/// [`FluentMessage`]: ./struct.FluentMessage.html
/// [`FluentBundle`]: ./struct.FluentBundle.html
/// [`FluentResource`]: ./struct.FluentResource.html
/// [`get_message`]: ./struct.FluentBundle.html#method.get_message
/// [`format_pattern`]: ./struct.FluentBundle.html#method.format_pattern
/// [`add_resource`]: ./struct.FluentBundle.html#method.add_resource
/// [`Cow<str>`]: http://doc.rust-lang.org/std/borrow/enum.Cow.html
/// [`Rc<FluentResource>`]: https://doc.rust-lang.org/std/rc/struct.Rc.html
/// [`Arc<FluentResource>`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
/// [`LanguageIdentifier`]: https://crates.io/crates/unic-langid
/// [`Vec<FluentError>`]: ./enum.FluentError.html
pub struct FluentBundle<R> {
pub locales: Vec<LanguageIdentifier>,
pub(crate) resources: Vec<R>,
Expand Down Expand Up @@ -236,10 +268,28 @@ impl<R> FluentBundle<R> {
}
}

/// When formatting patterns, `FluentBundle` inserts
/// Unicode Directionality Isolation Marks to indicate
/// that the direction of a placeable may differ from
/// the surrounding message.
///
/// This is important for cases such as when a
/// right-to-left user name is presented in the
/// left-to-right message.
///
/// In some cases, such as testing, the user may want
/// to disable the isolating.
pub fn set_use_isolating(&mut self, value: bool) {
self.use_isolating = value;
}

/// This method allows to specify a function that will
/// be called on all textual fragments of the pattern
/// during formatting.
///
/// This is currently primarly used for pseudolocalization,
/// and `fluent-pseudo` crate provides a function
/// that can be passed here.
pub fn set_transform<F>(&mut self, func: Option<F>)
where
F: Fn(&str) -> Cow<str> + Send + Sync + 'static,
Expand Down
64 changes: 49 additions & 15 deletions fluent-bundle/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
//! Fluent is a localization system designed to improve how software is translated.
//! Fluent is a modern localization system designed to improve how software is translated.
//!
//! The Rust implementation provides the low level components for syntax operations, like parser
//! and AST, and the core localization struct - `FluentBundle`.
//! and AST, and the core localization struct - [`FluentBundle`].
//!
//! `FluentBundle` is the low level container for storing and formatting localization messages. It
//! is expected that implementations will build on top of it by providing language negotiation
//! between user requested languages and available resources and I/O for loading selected
//! resources.
//! [`FluentBundle`] is the low level container for storing and formatting localization messages
//! in a single locale.
//!
//! This crate provides also a number of structures needed for a localization API such as [`FluentResource`],
//! [`FluentMessage`], [`FluentArgs`], and [`FluentValue`].
//!
//! Together, they allow implementations to build higher-level APIs that use [`FluentBundle`]
//! and add user friendly helpers, framework bindings, error fallbacking,
//! language negotiation between user requested languages and available resources,
//! and I/O for loading selected resources.
//!
//! # Example
//!
//! ```
//! use fluent_bundle::{FluentBundle, FluentValue, FluentResource, FluentArgs};
//!
//! // Used to provide a locale for the bundle.
//! use unic_langid::langid;
//!
//! let ftl_string = String::from("
//! hello-world = Hello, world!
//! intro = Welcome, { $name }.
//! ");
//! let res = FluentResource::try_new(ftl_string)
//! .expect("Could not parse an FTL string.");
//! .expect("Failed to parse an FTL string.");
//!
//! let langid_en = langid!("en-US");
//! let mut bundle = FluentBundle::new(&[langid_en]);
Expand All @@ -28,36 +36,62 @@
//! .add_resource(res)
//! .expect("Failed to add FTL resources to the bundle.");
//!
//! let msg = bundle.get_message("hello-world").expect("Message doesn't exist.");
//! let msg = bundle.get_message("hello-world")
//! .expect("Message doesn't exist.");
//! let mut errors = vec![];
//! let pattern = msg.value.expect("Message has no value.");
//! let pattern = msg.value
//! .expect("Message has no value.");
//! let value = bundle.format_pattern(&pattern, None, &mut errors);
//!
//! assert_eq!(&value, "Hello, world!");
//!
//! let mut args = FluentArgs::new();
//! args.insert("name", FluentValue::from("John"));
//!
//! let msg = bundle.get_message("intro").expect("Message doesn't exist.");
//! let msg = bundle.get_message("intro")
//! .expect("Message doesn't exist.");
//! let mut errors = vec![];
//! let pattern = msg.value.expect("Message has no value.");
//! let value = bundle.format_pattern(&pattern, Some(&args), &mut errors);
//!
//! // The FSI/PDI isolation marks ensure that the direction of
//! // the text from the variable is not affected by the translation.
//! assert_eq!(value, "Welcome, \u{2068}John\u{2069}.");
//! ```
//!
//! # Ergonomics & Higher Level APIs
//!
//! Reading the example, you may notice how verbose it feels.
//! Many core methods are fallible, others accumulate errors, and there
//! are intermediate structures used in operations.
//!
//! This is intentional as it serves as building blocks for variety of different
//! scenarios allowing implementations to handle errors, cache and
//! optimize results.
//!
//! At the moment it is expected that users will use
//! the `fluent-bundle` crate directly, while the ecosystem
//! matures and higher level APIs are being developed.
//!
//! [`FluentBundle`]: ./bundle/struct.FluentBundle.html
//! [`FluentResource`]: ./bundle/struct.FluentResource.html
//! [`FluentMessage`]: ./bundle/struct.FluentMessage.html
//! [`FluentArgs`]: ./bundle/struct.FluentArgs.html
//! [`FluentValue`]: ./bundle/struct.FluentValue.html

#[macro_use]
extern crate rental;
#[macro_use]
extern crate failure_derive;

pub mod bundle;
pub mod entry;
pub mod errors;
mod bundle;
mod entry;
mod errors;
pub mod resolve;
pub mod resource;
mod resource;
pub mod types;

pub use bundle::{FluentArgs, FluentBundle, FluentMessage};
pub use resource::FluentResource;
pub use bundle::{FluentBundle, FluentMessage, FluentArgs};
pub use types::FluentValue;
pub use errors::FluentError;
1 change: 1 addition & 0 deletions fluent-bundle/src/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ rental! {
}
}

/// A resource containing a list of localization messages.
#[derive(Debug)]
pub struct FluentResource(rentals::FluentResource);

Expand Down
4 changes: 2 additions & 2 deletions fluent-bundle/tests/resolver_fixtures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use std::iter;
use std::path::Path;
use std::str::FromStr;

use fluent_bundle::bundle::FluentArgs;
use fluent_bundle::errors::FluentError;
use fluent_bundle::FluentArgs;
use fluent_bundle::FluentError;
use fluent_bundle::resolve::ResolverError;
use fluent_bundle::{FluentBundle as FluentBundleGeneric, FluentResource, FluentValue};
use rand::distributions::Alphanumeric;
Expand Down
2 changes: 1 addition & 1 deletion fluent-bundle/tests/types_test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use fluent_bundle::resolve::Scope;
use fluent_bundle::types::FluentValue;
use fluent_bundle::FluentValue;
use fluent_bundle::FluentBundle;
use fluent_bundle::FluentResource;
use unic_langid::langid;
Expand Down
2 changes: 1 addition & 1 deletion fluent-fallback/tests/localization_test.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use elsa::FrozenMap;
use fluent_bundle::resource::FluentResource;
use fluent_bundle::FluentResource;
use fluent_bundle::FluentBundle;
use fluent_fallback::Localization;
use unic_langid::LanguageIdentifier;
Expand Down

0 comments on commit 817c2cb

Please sign in to comment.