# Implementing localization with i18n gem

**URL:** <https://discourse.hanakai.org/t/implementing-localization-with-i18n-gem/973>\
**Category:** questions\
**Created:** [May 22, 2024, 6:37am UTC](https://discourse.hanakai.org/t/implementing-localization-with-i18n-gem/973 "2024-05-22T06:37:21Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![sebastjan\_hribar](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/sebastjan_hribar/32/323_2.png) [@sebastjan\_hribar](https://discourse.hanakai.org/u/sebastjan_hribar)\
**Post date:** [May 22, 2024, 6:37am UTC](https://discourse.hanakai.org/t/implementing-localization-with-i18n-gem/973/1 "2024-05-22T06:37:21Z")

</div>

Hi all, I’m struggling a bit with the implementation of i18n gem and its functionalities. I have it in my 1.3 app as per this post: [Using I18n with Hanami - Bounga’s Home](https://www.bounga.org/ruby/2016/10/08/using-i18n-with-hanami/).

With 2.1 I simply wanted this as a component, but it’s not available in the view. How would I have to implement it to be available on the app level?

Also, in 1.3 I haven’t actually separated locales per individual app, even though I have several, because of the yml key names pattern. In 2.1 in might be useful to consider separation by slices as well.

Edit:  
What I’ve also added was an error message translator module to accommodate for Slovenian specifics 🙂

```auto
# coding: utf-8
module Translator
  private

  def translate_err_mess(incomming_error_messages)
    parsed_messages = {}
    translated_messages = []

    # Main_key would ussualy be an entity
    # Example {:contact=>{:name=>["must be filled", "size cannot be less than 3"]}
    incomming_error_messages.each do |main_key, main_key_errors|
      @entity = main_key
      main_key_errors.each do |field, messages|
        parsed_messages[field] = messages
      end
    end

    parsed_messages.each do |form_field, err_messages|
      subject = I18n.t ("#{@entity}_#{form_field}")
      field_element = I18n.t :err_field

      err_messages.each do |err_message|
        translated_messages << "#{field_element.capitalize} \"#{subject.capitalize}\" #{err_message}."
      end
    end
    return translated_messages
  end

end

::Hanami::Controller.configure do
  prepare do
    include Translator
  end
end

```

---

<div class="post-metadata">

**Author:** ![timriley](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/timriley/32/621_2.png) [@timriley](https://discourse.hanakai.org/u/timriley)\
**Post date:** [May 22, 2024, 11:57pm UTC](https://discourse.hanakai.org/t/implementing-localization-with-i18n-gem/973/2 "2024-05-22T23:57:01Z")

</div>

Hey @sebastjan_hribar, thanks for this question!

I did a simple i18n integration in a Hanami-style app quite a while ago. I set it up via a provider:

```ruby
# config/providers/i18n.rb

Hanami.app.register_provider :i18n, namespace: true do
  prepare do
    require "i18n"
  end

  start do
    # TODO: ensure these match your actual paths (e.g. if you use app/ instead)
    load_paths = Dir["#{target.root}/slices/ **/config/locales/** /*.yml"]

    I18n.load_path += load_paths
    I18n.backend.load_translations

    register :t, I18n.method(:t)
  end
end

```

This registers an `"i18n.t"` component into the app container.

You can then use that as a dep for any class to do translations:

```ruby
module MyApp
  class MyClass
    include Deps["i18n.t"]

    def some_method
      t.call("i18n.key.here")
    end
  end
end

```

If the callable `t` isn’t your jam, you can of course register the `I18n` class itself in the container (giving you an `"i18n"` component name and `i18n.t("...")` for your call sites), or some other kind of wrapper object built to your liking.

But let’s stick with `"i18n.t"` for now and see how we can get this into your views. To do this, you’ll want to add it as a dep for your view context class. Create a `views/context.rb` in your app or slice (if you don’t have one already), and then include the dep:

```ruby
# app/views/context.rb

module MyApp
  module Views
    class Context < Hanami::View::Context
      include Deps["i18n.t"]
    end
  end
end

```

With this done, you should now be able to call `t.call("...")` in your templates, and `context.t.call("...")` in your parts.

See [this guide](https://guides.hanamirb.org/v2.1/views/context/) for more details on the Hanami view context.

I hope this helps! Let me know how you go and if you have any other questions along the way.

---

<div class="post-metadata">

**Author:** ![sebastjan\_hribar](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/sebastjan_hribar/32/323_2.png) [@sebastjan\_hribar](https://discourse.hanakai.org/u/sebastjan_hribar)\
**Post date:** [May 23, 2024, 7:28pm UTC](https://discourse.hanakai.org/t/implementing-localization-with-i18n-gem/973/3 "2024-05-23T19:28:41Z")

</div>

Hi @timriley,

thank you very much for the code. I’ve implemented the provider and context as shown and it works as expected. By including the dependency in the action, I have access to `t.call` there as well.

I’m setting the default locale in the provider and later on I’ll have the default locale read from the DB like so:

```auto
Hanami.app.register_provider :i18n, namespace: true do
  prepare do
    require "i18n"
  end

  start do
    lang_setting = 1 # Change later to read from settings repo.

    I18n.available_locales = :en, :sl
    I18n.default_locale = :sl if lang_setting == 1

    load_paths = Dir["#{target.root}/app/translations/locale/*.yml"]

    I18n.load_path += load_paths
    I18n.backend.load_translations

    register :t, I18n.method(:t)
  end
end

```

I’m sure it’s clear by now based on my questions that I stumble about with the new concepts, so here is another question regarding i18n as a provider.

This is in my initializer in my 1.3 app:

```auto
existing_language = SettingRepository.new.get_default_language
  if existing_language
    I18n.default_locale = SettingRepository.new.get_default_language.value.to_sym
  else
    I18n.default_locale = :en
  end

```

If I read the guides right I’ll be able to do the same in the provider, right? Instead of current placeholder line `lang_setting = 1 # Change later to read from settings repo.`

The idea is, the user selects the UI language as they use the app.

---

<div class="post-metadata">

**Author:** ![timriley](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/timriley/32/621_2.png) [@timriley](https://discourse.hanakai.org/u/timriley)\
**Post date:** [May 24, 2024, 12:02am UTC](https://discourse.hanakai.org/t/implementing-localization-with-i18n-gem/973/4 "2024-05-24T00:02:13Z")

</div>

Thanks for the extra info, @sebastjan_hribar 🙂

Personally, I wouldn’t recommend doing a database lookup in the i18n provider. This couples it to your persistence layer in a way that (to me) doesn’t feel right for an otherwise simple subsystem.

Are you looking to set a locale per user based on preferences they have saved into the database?

In this case, I think doing this in your base action class might make more sense. Something like this:

```ruby
module MyApp
  class Action < Hanami::Action
    include Deps["setting_repo"]

    before :set_locale

    private

    def set_locale
      # TODO: make `current_user` exist
      # TODO: replace setting_repo logic with your own

      I18n.locale = setting_repo.locale_for_user(current_user)
    end
  end
end

```

---

<div class="post-metadata">

**Author:** ![timriley](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/timriley/32/621_2.png) [@timriley](https://discourse.hanakai.org/u/timriley)\
**Post date:** [May 24, 2024, 12:14am UTC](https://discourse.hanakai.org/t/implementing-localization-with-i18n-gem/973/5 "2024-05-24T00:14:48Z")

</div>

And if you wanted to make the i18n subsystem more encapsulated, I think that registering the whole `I18n` class as your `"i18n"` component would make that action callback a little cleaner, i.e. with it only needing to interact with the registered component, rather than multiple places in your app needing to be aware of the `I18n` constant:

```ruby
module MyApp
  class Action < Hanami::Action
    include Deps["i18n", "setting_repo"]

    before :set_locale

    private

    def set_locale
      # Now using an i18n dep, rather than referencing the class
      i18n.locale = setting_repo.locale_for_user(current_user)
    end
  end
end

```

---

<div class="post-metadata">

**Author:** ![sebastjan\_hribar](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.hanakai.org/sebastjan_hribar/32/323_2.png) [@sebastjan\_hribar](https://discourse.hanakai.org/u/sebastjan_hribar)\
**Post date:** [May 24, 2024, 4:11am UTC](https://discourse.hanakai.org/t/implementing-localization-with-i18n-gem/973/6 "2024-05-24T04:11:54Z")

</div>

Thank you again, this is a cleaner approach. I might have overcomplicated in my existing 1.3 app. I have a rake task with all initial settings the app requires that run when the app boots with docker-compose. One of those is also the default language setting. That is why I have the initializer coupled with the corresponding repo. However, I see now it’s a redundant step anyway. 🙂
