PlumeKit Documentation

Translations

PlumeKit resolves a request's language once and makes a t() function available to handlers and views, so you don't pass a locale around. Strings are compiled into the app (no runtime file parsing), so the same translations work natively and on the edge.

Setup

Put one JSON file per language in Translations/, named by locale. Each is a flat map of key to string:

JSON
// Translations/en.json
{ "welcome.title": "Welcome back", "greeting": "Hello, {name}" }
JSON
// Translations/pt.json
{ "welcome.title": "Bem-vindo de volta", "greeting": "Olá, {name}" }

The build compiles them into a plumeKitTranslations value automatically. Set the fallback language, used when a request matches none, in plumekit.toml:

TOML
[i18n]
default = "en"

Scaffolded apps already register the middleware in buildApp() (app.use(localization(plumeKitTranslations))), so once you add the files, t() works.

Using it

In a handler or a view, call t("key"). Placeholders in {braces} are filled from named arguments:

SWIFT
// handler
return .text(t("greeting", ["name": user.name]))
PLUME
<h1>{t("welcome.title")}</h1>
<p>{t("greeting", name: user.name)}</p>

A missing key renders the key itself (so an untranslated string is visible, not blank), and a missing placeholder is left in place.

Choosing the language

The middleware picks the language in this order:

  1. a ?lang= query parameter,
  2. a locale cookie,
  3. the request's Accept-Language header,
  4. the [i18n] default.

To honor a signed-in user's saved preference, override the active language for the rest of the request:

SWIFT
app.use { request, next in
    if let user = try await currentUser(request), !user.language.isEmpty {
        useLocale(user.language)
    }
    return try await next(request)
}

currentLocale returns the active language if you need it.

In client scripts

Inside an @script block you can call t("key") too. In the compiled build the framework injects the active language's strings into the page, so the client t() returns the right translation:

PLUME
<button @script { on click { window.alert(t("confirm.delete")) } }>Delete</button>

This works in the compiled build only. Under the interpreter t() is not defined, so if you render templates that way, translate in the view instead and read the value from the DOM:

PLUME
<button data-confirm="{t("confirm.delete")}"
        @script { on click { window.alert(el.dataset.confirm) } }>Delete</button>

The data-attribute form is translated server-side, so it works in every render mode.