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:
# 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:
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:
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!