diff --git a/lib/i18n.rb b/lib/i18n.rb index 96f58b5d..b233be3c 100644 --- a/lib/i18n.rb +++ b/lib/i18n.rb @@ -176,15 +176,13 @@ def eager_load! # from the argument values passed to #translate. Therefor your lambdas should # always return the same translations/values per unique combination of argument # values. - def translate(*args) - options = args.last.is_a?(Hash) ? args.pop.dup : {} - key = args.shift - backend = config.backend - locale = options.delete(:locale) || config.locale - handling = options.delete(:throw) && :throw || options.delete(:raise) && :raise # TODO deprecate :raise - + def translate(key = nil, *, throw: false, raise: false, locale: nil, **options) # TODO deprecate :raise + locale ||= config.locale + raise Disabled.new('t') if locale == false enforce_available_locales!(locale) + backend = config.backend + result = catch(:exception) do if key.is_a?(Array) key.map { |k| backend.translate(locale, k, options) } @@ -192,7 +190,12 @@ def translate(*args) backend.translate(locale, key, options) end end - result.is_a?(MissingTranslation) ? handle_exception(handling, result, locale, key, options) : result + + if result.is_a?(MissingTranslation) + handle_exception((throw && :throw || raise && :raise), result, locale, key, options) + else + result + end end alias :t :translate @@ -204,7 +207,9 @@ def translate!(key, options = EMPTY_HASH) alias :t! :translate! # Returns true if a translation exists for a given key, otherwise returns false. - def exists?(key, locale = config.locale) + def exists?(key, _locale = nil, locale: _locale) + locale ||= config.locale + raise Disabled.new('exists?') if locale == false raise I18n::ArgumentError if key.is_a?(String) && key.empty? config.backend.exists?(locale, key) end @@ -260,37 +265,40 @@ def exists?(key, locale = config.locale) # I18n.transliterate("Jürgen") # => "Juergen" # I18n.transliterate("Jürgen", :locale => :en) # => "Jurgen" # I18n.transliterate("Jürgen", :locale => :de) # => "Juergen" - def transliterate(*args) - options = args.pop.dup if args.last.is_a?(Hash) - key = args.shift - locale = options && options.delete(:locale) || config.locale - handling = options && (options.delete(:throw) && :throw || options.delete(:raise) && :raise) - replacement = options && options.delete(:replacement) + def transliterate(key, *, throw: false, raise: false, locale: nil, replacement: nil, **options) + locale ||= config.locale + raise Disabled.new('transliterate') if locale == false enforce_available_locales!(locale) + config.backend.transliterate(locale, key, replacement) rescue I18n::ArgumentError => exception - handle_exception(handling, exception, locale, key, options || {}) + handle_exception((throw && :throw || raise && :raise), exception, locale, key, options) end # Localizes certain objects, such as dates and numbers to local formatting. - def localize(object, options = nil) - options = options ? options.dup : {} - locale = options.delete(:locale) || config.locale - format = options.delete(:format) || :default + def localize(object, locale: nil, format: nil, **options) + locale ||= config.locale + raise Disabled.new('l') if locale == false enforce_available_locales!(locale) + + format ||= :default config.backend.localize(locale, object, format, options) end alias :l :localize # Executes block with given I18n.locale set. def with_locale(tmp_locale = nil) - if tmp_locale + if tmp_locale == nil + yield + else current_locale = self.locale - self.locale = tmp_locale + self.locale = tmp_locale + begin + yield + ensure + self.locale = current_locale + end end - yield - ensure - self.locale = current_locale if tmp_locale end # Merges the given locale, key and scope into a single array of keys. @@ -314,7 +322,7 @@ def locale_available?(locale) # Raises an InvalidLocale exception when the passed locale is not available. def enforce_available_locales!(locale) - if config.enforce_available_locales + if locale != false && config.enforce_available_locales raise I18n::InvalidLocale.new(locale) if !locale_available?(locale) end end diff --git a/lib/i18n/config.rb b/lib/i18n/config.rb index 10645017..ea3dd1ee 100644 --- a/lib/i18n/config.rb +++ b/lib/i18n/config.rb @@ -7,7 +7,7 @@ class Config # The only configuration value that is not global and scoped to thread is :locale. # It defaults to the default_locale. def locale - defined?(@locale) && @locale ? @locale : default_locale + defined?(@locale) && @locale != nil ? @locale : default_locale end # Sets the current locale pseudo-globally, i.e. in the Thread.current hash. diff --git a/lib/i18n/exceptions.rb b/lib/i18n/exceptions.rb index a9d59dd6..7f4f1d2d 100644 --- a/lib/i18n/exceptions.rb +++ b/lib/i18n/exceptions.rb @@ -15,6 +15,20 @@ def call(exception, _locale, _key, _options) class ArgumentError < ::ArgumentError; end + class Disabled < ArgumentError + def initialize(method) + super(<<~MESSAGE) + I18n.#{method} is currently disabled, likely because your application is still in it's loading phase. + + This method is meant to display text in the user locale, so calling it before the user locale has + been set is likely to display text from the wrong locale to some users. + + If you have a legitimate reason to access i18n data outside of the user flow, you can do so by passing + the desired locale explictly with the `locale` argument, e.g. `I18n.#{method}(..., locale: :en)` + MESSAGE + end + end + class InvalidLocale < ArgumentError attr_reader :locale def initialize(locale) diff --git a/test/i18n_test.rb b/test/i18n_test.rb index c7e6b437..fbe7faf1 100644 --- a/test/i18n_test.rb +++ b/test/i18n_test.rb @@ -249,6 +249,16 @@ def setup assert_equal "No", I18n.t(false) end + test "translate raises Disabled if locale is false" do + I18n.with_locale(false) do + assert_raise I18n::Disabled do + I18n.t('foo') + end + + assert_equal 'translation missing: en.foo', I18n.t('foo', locale: :en) + end + end + test "available_locales can be replaced at runtime" do begin I18n.config.enforce_available_locales = true @@ -290,6 +300,16 @@ def setup assert_equal false, I18n.exists?(:bogus, :nl) end + test "exists? raises Disabled if locale is false" do + I18n.with_locale(false) do + assert_raise I18n::Disabled do + I18n.exists?(:bogus) + end + + assert_equal false, I18n.exists?(:bogus, :nl) + end + end + test "localize given nil raises an I18n::ArgumentError" do assert_raise(I18n::ArgumentError) { I18n.l nil } end @@ -311,6 +331,14 @@ def setup end end + test "localize raises Disabled if locale is false" do + I18n.with_locale(false) do + assert_raise I18n::Disabled do + I18n.l(Time.now) + end + end + end + test "can use a lambda as an exception handler" do begin previous_exception_handler = I18n.exception_handler