About
Compile-time validated i18n library for Rust. It uses the ICU4X crate from Unicode and follows some of the ICU MessageFormat syntax: {name}, {count, plural, ...}, {gender, select, ...}. Unknown keys, missing/extra arguments, wrong argument types, and missing other arms are compile errors.
[!IMPORTANT]
rust_intlis still in development but has been tested in production apps. We plan to support more of the ICU message syntax and allow other locales file formats (e.g. yml, toml) and backend customization (e.g. translations loaded/fetched at startup with no compile-time validation).
Features
t!macro with compile-time validation- Typesafe auto-generated functions (
t_your_key(args)) - Function namespacing with
#[t_ns(namespace = "prefix")] - Global locale for single-user apps:
set_current_locale!(Locale::Fr) - Partial support of ICU Message Syntax
Installation
or
= "0.1"
Usage
The load! macro will generate many types in your crates, including Locale, Lang, get_current_locale!, set_current_locale!, etc.
// main.rs
// loads locales/[locale]/[namespace].json by default
load!;
// build.rs
To load a different directory:
load!;
Translations
t! macro
The main way to translate is using the t! macro. The macro always returns a String.
// Using the current or default locale
t!;
// You can override the locale with "locale = ..."
t!;
// or
t!;
You can also use any type that implement LocaleProvider as the first argument:
get_current_locale! and set_current_locale!
For single-user apps, like desktop apps, usually only one locale can be used at a time so there is no need to use t!(ctx, "key") everywhere.
Instead, you can use
// Change the "global" locale for every t! that dont have "locale = ..."
set_current_locale!;
t!; // will be in fr
// Get the locale if needed
let locale = get_current_locale!;
Namespacing a whole function
#[t_ns(namespace = "...")] prefixes every t!() call inside the function. Works on async fn too. Example:
Instead of:
async
You can use:
async
However if you need keys from outside the prefix, you will need / to start back at the root:
t!; // -> "common.another_path.key"
Lang & auto-generated functions
Every keys from your translations are also translated into static functions on Locale & Lang.
t!;
Fr.t_hello_world; // all keys are prefixed with t_
A Lang struct is also available:
let lang = new;
lang.t_hello_world_with_args;
lang.t_key() returns &'static str for no-argument keys (zero allocation) and String for keys with arguments.
Message syntax
"{name}" // interpolation, any Display type
"{count, plural, =0 {none} one {1 item} other {{count} items}}" // CLDR plural; `other` required
"{n, selectordinal, one {1st} two {2nd} few {3rd} other {{n}th}}" // ordinal plural
"{gender, select, male {He} female {She} other {They}}" // `other` required
"{v, number}" // and {v, date} / {v, time}: plain Display interpolation
"Use '{name}' literally" // '{...}' escapes braces as plain text
Numbers
// ICU formatting, english: 1000 -> "1,000", french: 1000 -> "1 000"
t!;
// custom display string
use NumberArg;
t!;