# Bilingual sites with Hanami

**URL:** <https://discourse.hanakai.org/t/bilingual-sites-with-hanami/1534>\
**Category:** sharing\
**Created:** [September 14, 2026, 11:24am UTC](https://discourse.hanakai.org/t/bilingual-sites-with-hanami/1534 "2026-09-14T11:24:19Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![evenreven](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/evenreven/32/1189_2.png) [@evenreven](https://discourse.hanakai.org/u/evenreven)\
**Post date:** [September 14, 2026, 11:24am UTC](https://discourse.hanakai.org/t/bilingual-sites-with-hanami/1534/1 "2026-09-14T11:24:19Z")

</div>

The Rails application I want to rewrite in Hanami (inshallah) is bilingual and uses Mobility for translated database content. Since there’s no ROM plugin for Mobility, and I wouldn’t know how to write one (and sadly can’t commit to it, time-wise), I’ve been looking into whether I need Mobility at all.

It struck me if your needs are simple, like mine (only two languages and using Mobility’s column backend (sometime referred to as Traco-style as opposed to Globalize-style separate table), with the locale code in the column names, in my case `title_en` and `title_nb`), a little metaprogramming may be enough.

On Rails, this would get messy, but with Hanami’s architecture, we already have a place for this (the struct base class), so it’s just a question of specifying the translated attributes we want in each struct. My goal is to be able to call `book.title` in a view and get the title in the currently chosen language, i.e. dynamically map it to `title_nb` or `title_en`.

This is super hacky and only works with two languages, and only with column backend style translated db content, but it’s enough for me. In the struct base class I put this:

```ruby
# app/db/struct.rb
require "hanami/db/struct"

module Bookshelf
  module DB
    class Struct < Hanami::DB::Struct
      def i18n
        Hanami.app["i18n"]
      end

      def available_locales
        i18n.available_locales
      end

      def current_locale
        i18n.locale
      end

      def other_locale
        available_locales.find { |locale| locale != current_locale }
      end

      def locale_specific_attr(locale, attribute) = public_send(:"#{attribute}_#{locale}")

      def generic_attribute(attribute)
        locale_specific_attr(current_locale, attribute).to_s
      end

      def generic_attribute_with_fallback(attribute)
        attribute_in_current_locale = locale_specific_attr(current_locale, attribute)
        return attribute_in_current_locale.to_s unless available_locales.size == 2

        attribute_in_other_locale = locale_specific_attr(other_locale, attribute)

        if attribute_in_current_locale.nil?
          attribute_in_other_locale.to_s
        else
          attribute_in_current_locale.to_s
        end
      end
    end
  end
end

```

Then in the specific struct:

```ruby
module Bookshelf
  module Structs
    class Book < Bookshelf::DB::Struct
      def title = generic_attribute_with_fallback("title")
    end
  end
end

```

I’m sure this can be rewritten to be less hacky, but since you can’t include Deps in a struct, I couldn’t find any other way except getting the values from the `Hanami.app["i18n"]` object. With this, I was able to write `book.title` in the view if I wrap the render call in a with\_locale block. Like this:

```ruby
module Bookshelf
  module Actions
    module Studies
      class Index < Bookshelf::Action
        def handle(request, response)
          i18n.with_locale(request.params[:locale]) do
            response.render(view, locale: request.params[:locale])
          end
        end
      end
    end
  end
end

```

Please share if there’s a more elegant (or performant; I haven’t tested this at scale) way of doing this, but since this was my biggest blocker (except time) when it comes to rewriting a Rails app to Hanami, I’m just happy it seems to work!
