Hanami supports internationalization and localization for your app’s content via the i18n gem.
When the i18n gem is bundled, Hanami registers an "i18n" component in your app (and each slice) that provides a self-contained translation backend. You can then translate and localize content from anywhere in your app, and use translate and localize helpers in your actions and views.
Enabling i18n
To enable i18n, add the i18n gem to your Gemfile:
After running bundle install, Hanami registers the "i18n" component in your app and any slices.
You can confirm this in the console (bundle exec hanami console):
Hanami.app["i18n"]
# => #<Hanami::Providers::I18n::Backend ...>
Translation files
Translations live in YAML, JSON or Ruby files under config/i18n/. Files are loaded for all locales they define, with the top-level key naming the locale:
# config/i18n/en.yml
en:
greeting: "Hello!"
messages:
welcome: "Welcome to Bookshelf"
# config/i18n/tl.yml
tl:
greeting: "Kumusta!"
messages:
welcome: "Maligayang pagdating sa Bookshelf"
You can organize translation files however you like under config/i18n/. They are matched by the glob config/i18n/**/*.{yml,yaml,json,rb}, so nested directories are loaded too.
Translating content
Use the "i18n" component to translate keys from your translation files:
Hanami.app["i18n"].t("messages.welcome")
# => "Welcome to Bookshelf"
Hanami.app["i18n"].t("greeting", locale: :tl)
# => "Kumusta!"
The component exposes the full i18n API, including translate (aliased to t), translate! (aliased to t!, which raises on missing translations), localize (aliased to l), exists? and transliterate.
You can inject the "i18n" component into your own classes using the Deps mixin:
include Deps["i18n"]
i18n.t("messages.welcome_named", name: name)
end
end
end
end
Localizing dates and times
Use localize (or its alias l) to format dates and times according to the current locale:
Hanami.app["i18n"].l(Date.new(2026, 5, 22), format: :short)
# => "22 May"
Hanami.app["i18n"].l(Time.now, format: :long)
# => "22 May 2026 9:05 am"
Hanami includes built-in English defaults for date.formats, date.day_names, date.month_names, time.formats and meridiem markers (“am” and “pm”), so localize works out of the box without any translation files.
You can override any of these defaults by setting the same keys in your own translation files:
# config/i18n/en.yml
en:
date:
formats:
short: "%d/%m/%Y"
The available formats and locale-dependent strftime codes (%a, %A, %b, %B, %p, %P) are resolved through the slice’s i18n backend, so they respect the current locale.
In your actions and views
The translate and localize helpers (along with their t and l aliases) are automatically available in your actions and in your view templates, parts and scopes.
In an action:
response.flash[:notice] = t("messages.post_created")
response.redirect_to routes.path(:posts)
end
end
end
end
end
In a view template:
See Helpers for full details, including HTML-safe keys and relative key lookup.
Switching the current locale
The current locale decides which translations are used. To change it, set locale:
Hanami.app["i18n"].locale = :tl
Each slice’s i18n backend keeps the current locale in thread-local storage, so setting it in one request never affects another running at the same time.
To run a block with a specific locale and have the previous locale restored afterwards (even if the block raises), use with_locale:
Hanami.app["i18n"].with_locale(:tl) do
Hanami.app["i18n"].t("greeting") # => "Kumusta!"
end
This is the recommended approach when setting the locale per-request (for example, from an action).
Next steps
- Configuration covers all the available i18n configuration options.
-
Helpers covers the
translateandlocalizeview helpers, including HTML safety and relative key lookup. - Slices covers how i18n works across slices and patterns for sharing translations.