# frozen_string_literal: true
require "mobility/util"
module Mobility
module Plugins
=begin
Falls back to one or more alternative locales in case no value is defined for a
given locale.
For +fallbacks: true+, Mobility will use the value of
{Mobility::Configuration#new_fallbacks} for the fallbacks instance. This
defaults to an instance of +I18n::Locale::Fallbacks+, but can be
configured (see {Mobility::Configuration}).
If a hash is passed to the +fallbacks+ option, a new fallbacks instance will be
created for the model with the hash defining additional fallbacks. To set a
default value for this hash, use set the value of `default_options[:fallbacks]`
in your Mobility configuration (see below).
In addition, fallbacks are disabled in certain situations. To explicitly disable
fallbacks when reading and writing, you can pass the fallback: false
option to the reader method. This can be useful to determine the actual
value of the translated attribute, including a possible +nil+ value.
The other situation where fallbacks are disabled is when the locale is
specified explicitly, either by passing a `locale` option to the accessor or by
using locale or fallthrough accessors. (See example below.)
You can also pass a locale or array of locales to the +fallback+ option to use
that locale or locales that read, e.g. fallback: :fr would fetch the
French translation if the value in the current locale was +nil+, whereas
fallback: [:fr, :es] would try French, then Spanish if the value in
the current locale was +nil+.
@see https://github.com/svenfuchs/i18n/wiki/Fallbacks I18n Fallbacks
@example With default fallbacks enabled (falls through to default locale)
class Post
extend Mobility
translates :title, fallbacks: true
end
I18n.default_locale = :en
Mobility.locale = :en
post = Post.new(title: "foo")
Mobility.locale = :ja
post.title
#=> "foo"
post.title = "bar"
post.title
#=> "bar"
@example With additional fallbacks enabled
class Post
extend Mobility
translates :title, fallbacks: { :'en-US' => 'de-DE', :pt => 'de-DE' }
end
Mobility.locale = :'de-DE'
post = Post.new(title: "foo")
Mobility.locale = :'en-US'
post.title
#=> "foo"
post.title = "bar"
post.title
#=> "bar"
@example Passing fallback option when reading value
class Post
extend Mobility
translates :title, fallbacks: true
end
I18n.default_locale = :en
Mobility.locale = :en
post = Post.new(title: "Mobility")
Mobility.with_locale(:fr) { post.title = "Mobilité" }
Mobility.locale = :ja
post.title
#=> "Mobility"
post.title(fallback: false)
#=> nil
post.title(fallback: :fr)
#=> "Mobilité"
@example Fallbacks disabled
class Post
extend Mobility
translates :title, fallbacks: { :'fr' => 'en' }, locale_accessors: true
end
I18n.default_locale = :en
Mobility.locale = :en
post = Post.new(title: "Mobility")
Mobility.locale = :fr
post.title
#=> "Mobility"
post.title(fallback: false)
#=> nil
post.title(locale: :fr)
#=> nil
post.title_fr
#=> nil
@example Setting default fallbacks across all models
Mobility.configure do |config|
# ...
config.default_options[:fallbacks] = { :'fr' => 'en' }
# ...
end
class Post
# Post will fallback from French to English by default
translates :title, fallbacks: true
end
=end
class Fallbacks < Module
# Applies fallbacks plugin to attributes. Completely disables fallbacks
# on model if option is +false+.
# @param [Attributes] attributes
# @param [Boolean] option
def self.apply(attributes, option)
attributes.backend_class.include(new(option)) unless option == false
end
def initialize(fallbacks_option)
define_read(convert_option_to_fallbacks(fallbacks_option))
end
private
def define_read(fallbacks)
define_method :read do |locale, fallback: true, **options|
return super(locale, options) if !fallback || options[:locale]
locales = fallback == true ? fallbacks[locale] : [locale, *fallback]
locales.each do |fallback_locale|
value = super(fallback_locale, options)
return value if Util.present?(value)
end
super(locale, options)
end
end
def convert_option_to_fallbacks(option)
if option.is_a?(Hash)
Mobility.new_fallbacks(option)
elsif option == true
Mobility.new_fallbacks
else
Hash.new { [] }
end
end
end
end
end