;; Simple lookup
[:t :user/sign-in]
;; With interpolation params
[:t :user/greeting {:name "Alice"}]
;; With plural count (4th element)
[:t :user/items {:n 3} 3]
Internationalisation for Wagoe apps: marker-based translation, locale chain resolution, EDN catalogues, and Ring middleware. Follows ADR-013.
| Namespace | Purpose |
|---|---|
| Pure |
|
|
|
|
|
|
|
|
| Integrant |
;; Simple lookup
[:t :user/sign-in]
;; With interpolation params
[:t :user/greeting {:name "Alice"}]
;; With plural count (4th element)
[:t :user/items {:n 3} 3]
Markers are resolved by wagoe.i18n.shell.render/resolve-markers during render. They can appear anywhere in a Hiccup tree. Strings pass through unchanged.
wrap-i18n builds an ordered locale chain from the Ring request:
User locale — [:user :language], else [:session :user :language]
Tenant locale — [:tenant :settings :language], else [:tenant :default-language]
Default locale from Integrant config
Hard fallback: :en
Missing keys degrade to (str key) (e.g. "user/sign-in"). They never throw.
{:user/sign-in "Sign In"
:user/greeting {:one "Hello {name}" :many "Hello everyone"}
:user/items {:zero "No items" :one "{n} item" :many "{n} items"}
:common/button-cancel "Cancel"}
Key namespacing conventions: :common/ for shared UI strings, :user/, :admin/, :search/, etc. per module. English and Dutch catalogues are included out of the box.
;; config.edn
:wagoe/i18n {:catalogue-paths ["wagoe/i18n/translations"
"my_app/i18n/translations"] ; or one :catalogue-path
:default-locale :en}
Without a :wagoe/i18n entry the component loads the framework’s catalogue
with :en as default. :wagoe/i18n-http-middleware builds wrap-i18n from it,
and the HTTP handler applies it. Pass {:dev? true} to render or
resolve-markers to wrap resolved markers in [:span {:data-i18n "…"}] for
browser inspection; :dev? on the component only logs each request’s locale chain.
wagoe.i18n.shell.middleware/wrap-i18n injects into every Ring request:
| Key | Description |
|---|---|
| Ordered vector of locales to try, e.g. |
| Translation function — |
;; In a handler
(let [t (get request :i18n/t identity)]
(t :user/sign-in)
(t :user/greeting {:name "Alice"})
(t :user/items {:n 3} 3))
Add libs/i18n/resources/wagoe/i18n/translations/fr.edn.
Add the locale to load-catalogue’s default `[:en :nl]: the component
passes no locale list, so other files are not loaded.
Run bb i18n:missing to see which keys need translating.
# Find a key by substring or exact keyword
bb i18n:find "Sign in"
bb i18n:find :user/sign-in
# Scan core/ui.clj files for unexternalised string literals (CI gate)
bb i18n:scan
# Report translation gaps (keys in en.edn missing from other locales)
bb i18n:missing
# Report catalogue keys not referenced in any source file
bb i18n:unused
clojure -M:test :i18n
# Unit tests only
clojure -M:test :i18n --focus-meta :unit
Can you improve this documentation? These fine people already did:
Thijs Creemers & thijscreemersEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |