Molfar
Molfar

Reputation: 1501

Set per request i18n fallbacks in Rails?

I have a multi-country Rails app. And I need to set per request i18n fallbacks schema. Is it possible and how to achieve this?

UPD

class Country < ApplicationRecord
  # String attribute "languages" (array)
end

class CountriesController < ApplicationController

  def show
    @country = Country.find params[:id]

    I18n.fallbacks = {
      @country.languages.first => @country.languages.second
    } # This does not work

    render 'show'
  end
end

Upvotes: 0

Views: 672

Answers (2)

rmlockerd
rmlockerd

Reputation: 4136

Experimenting a bit (with Rails 6), it is possible to change the fallbacks using the Simple (default) backend, but doing so is not thread-safe and will likely cause problems if you do it on a per-request basis. It's somewhat counter-intuitive -- setting I18n.locale is the documented way to dynamically set locale per request, so it's natural to assume fallbacks would work the same way. However, from the the i18n source:

The only configuration value that is not global and scoped to thread is :locale.

Even that isn't very clearly worded. But indeed locale is defined as an instance variable, and all other configuration attributes are @@ class variables.

The Rails guide for I18n says that the Simple (default) backend was designed to only do the "simplest thing that could possibly work", but the framework allows for plugging in custom backends that go beyond that. So, the best way for you to achieve your result will be to find (or create) a backend that supports per-request fallbacks in a thread-safe way.


For reference, if someone does need to change a language fallback outside the initializer (again, that's globally), fallbacks.map() does that:

I18n.fallbacks.map(:ca => :"es-ES")

My original answer mentioned fiddling with the fallback hash directly, but using .map() preserves falling back to the default locale.

Upvotes: 1

Clemens Kofler
Clemens Kofler

Reputation: 1968

Adding to what rmlockerd said, due to fallbacks being a class variable, I recommend you restore your fallbacks after every request just to ensure that you're not accidentally leaking it to other requests in the same server instance. You could do it like so:

around_action :restore_i18n_fallbacks

# ...

private

def restore_i18n_fallbacks(&action)
  original_fallbacks = I18n.fallbacks
  begin
    action.call
  ensure
    I18n.fallbacks = original_fallbacks
  end
end

(Code not tested but should work or be fairly close to working.)

Upvotes: 1

Related Questions