phrasebook
Fluent localization for Rust web frontends: message catalogs per language, formatting with arguments and plurals, a fallback language, and the user's chosen language remembered in the browser.
An app declares its languages and catalogs once, in a Phrasebook, and
hands its components a Phrases handle: the phrasebook open at the
current language. Components look messages up with Phrases::text and
Phrases::msg. A library ships its own Catalog under a prefix,
which an app adds to its phrasebook and can override or translate, and
which the library falls back to when the app provides nothing.
The design is in docs/design.md.
Features
No feature is on by default. On docs.rs, items that need a feature are marked with it.
| Feature | Module | What it provides |
|---|---|---|
| (none) | Lang, Catalog, Source, Phrasebook, Phrases, Args, Value, args!, and the check test helpers |
|
browser |
browser |
the language remembered in localStorage, the browser's preferred languages, and Phrases::store |
yew023 |
yew023 |
yew 0.23: ImplicitClone and IntoPropValue impls, and the or_default and or_catalog helpers for components rendered without a provided handle |
koine01 |
koine01 |
conversions between Lang and koine 0.1's Language, Country and Locale, a warning for declared languages koine does not know, and Builder::language_named_by_koine |
Fluent and unic-langid are used underneath but appear nowhere in the API, so upgrading them is never a breaking change. A dependency that is not yet 1.0 and does appear in the API is named after its version in its feature and module, so a later version can be added beside it.
yew023 does not enable browser; an app in the browser enables both.
Example
use ;
const SV_SE: Lang = new;
static APP: Catalog = new;
let book = builder
.language
.language
.catalog
.build;
let phrases = book.phrases;
assert_eq!;
// Not translated yet: the fallback language, logged once.
assert_eq!;
// Not defined anywhere: the id, so the hole is visible.
assert_eq!;
let phrases = phrases.with_lang;
assert_eq!;
In an app's tests, check turns a catalog's problems (syntax errors,
duplicate ids, ids outside the prefix, messages that do not format, and
untranslated or extra messages) into failures; an app that translates a
library's messages in its own catalog checks parity with
check::parity_with, naming the library catalogs.
With yew
phrasebook ships no components and no hooks: the context type is
Phrases itself. The app's root owns the handle as app state and
provides it with yew's own ContextProvider<Phrases>; on a language change
it provides phrases.with_lang(lang) (and calls store on it, with
browser). Components read it with use_context::<Phrases>(), or
ctx.link().context::<Phrases>(..) in a struct component.
// The app's root, in `view`.
html!
// An app's component.
let phrases = or_default;
// A library's component, with its own catalog `L10N`.
let phrases = or_catalog;
A library's component falls back to its own catalog for messages the app
lacks, or to the catalog alone when nothing is provided. The yew023
module documents both forms in full, struct components included.
Examples
examples/tour.rs prints what an app's catalog, a library's catalog
with a backstop, plurals and the fallback language produce, in English
and Swedish:
examples/app is a small Yew app: the root owns and provides the
handle, a picker switches and remembers the language, and a library-style
component falls back to its own catalog. It is its own cargo workspace,
so it adds nothing to the crate's builds, and is built and served with
trunk:
&&