Reputation: 1501
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
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
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