Skip to main content

Module i18n

Module i18n 

Source
Expand description

Translations: locales/*.yml, %{name} interpolation, plurals, locale per request.

Works like Rails’ I18n.t: YAML files in locales/ compiled into the Worker, %{name} interpolation, CLDR plurals, fallback to the default locale and locale selection per request.

# locales/fr.yml (a YAML subset: nested keys and strings)
fr:
  posts:
    created: "Article créé."
    greeting: "Bonjour %{name} !"
    count:
      one: "%{count} article"     # CLDR categories: zero, one, two, few, many, other
      other: "%{count} articles"

An app declares its locales once in src/lib.rs (ocre g locale en fr writes it) and adds layer at the end of routes(); handlers then take the I18n extractor:

ⓘ
// Not compiled here: `locales!` includes `locales/en.yml` and `locales/fr.yml` from the app's root.
static LOCALES: ocre::i18n::Locales = ocre::locales!("en", "fr");
use std::sync::LazyLock;
use axum::{Router, routing::get};
use ocre::{Ctx, i18n::{Catalog, I18n, Locales}};

// `ocre::locales!("en", "fr")` expands to this, with the files' contents.
static LOCALES: Locales = LazyLock::new(|| {
    Catalog::load(&[
        ("en", "en:\n  posts:\n    count:\n      one: \"%{count} post\"\n      other: \"%{count} posts\"\n"),
        ("fr", "fr:\n  posts:\n    count:\n      one: \"%{count} article\"\n      other: \"%{count} articles\"\n"),
    ])
});

async fn home(i18n: I18n) -> String {
    i18n.t("posts.count").count(3).to_string() // "3 articles" for French visitors
}

fn routes() -> Router<Ctx> {
    Router::new()
        .route("/", get(home))
        // ocre:routes
        .layer(ocre::i18n::layer(&LOCALES))
}

// Outside requests (mailers, jobs):
assert_eq!(LOCALES.locale("fr").t("posts.count").count(3).to_string(), "3 articles");

In templates, translations are Display values that askama writes (escaped) straight into the page: <p>{{ i18n.t("posts.greeting").arg("name", user.name) }}</p>. Never mark them |safe; for a translation containing markup, end with .html(), which escapes the values only.

Beyond t: I18n::l formats dates and times, I18n::number and I18n::currency numbers, I18n::model_name and I18n::attribute name models and fields, I18n::error_message translates validation errors, and I18n::time_ago_in_words durations. Their texts come from the app’s files first, then from built-in translations (compiled in, no parsing) for English, French, German, Spanish, Italian, Portuguese and Dutch, under the rails-i18n keys (date.formats.short, errors.messages.blank…).

The files are parsed once per Worker instance, on the first translation, by a small parser for this YAML subset; lookups are a BTreeMap search. No D1, KV or other billed resource is used. I18n picks the locale from a {locale} path segment, then the locale cookie, then Accept-Language, then the default locale. A key missing from the request’s locale falls back to the default locale in release builds (ocre deploy); debug builds (ocre dev) show translation missing: fr.posts.created instead, so gaps are visible. ocre i18n missing lists them (see check).

Structs§

Catalog
All locales of an app, parsed: one table of dotted keys per locale code, the first being the default.
HtmlTranslation
A Translation written as HTML, from Translation::html: the text as is, every value escaped.
I18n
Axum extractor giving translations in the request’s locale, like Rails’ I18n.t with a per-request locale.
Translation
A translation being built by I18n::t, displayed (looked up and interpolated) when written.

Enums§

Problem
A problem in the locale files, found by check.

Constants§

LOCALE_COOKIE
Name of the cookie that remembers a visitor’s chosen locale.
LOCALE_PARAM
Name of the path parameter I18n reads the locale from.

Traits§

Count
A number Translation::count accepts: every integer type, and references to them.

Functions§

check
Checks locale files for syntax errors and keys missing from non-default locales.
layer
Makes the catalog available to the I18n extractor, as an axum layer.

Type Aliases§

Locales
The app’s translations: a Catalog parsed on first use, declared once as a static.