I want to isolate my test from internalization. I use rails 3.2.8 and rspec 2.11.1
I put this code to spec/support/translations.rb
module I18nHelpers
def with_translations(locale, translations)
I18n.backend.store_translations locale, translations
yield
ensure
I18n.reload!
end
end
RSpec.configure do |config|
config.include I18nHelpers
end
Then I test application helper:
describe ApplicationHelper do
context "messages" do
it "show body" do
with_translations :en, navigation: {messages: 'foo'} do
concat messages_navigation
assert_test 'span', 'foo'
end
end
end
end
But this test falls with message
Failure/Error: assert_select 'span', text: /foo/
MiniTest::Assertion:
</foo/> expected but was
<"Messages">.
'Messages' is from my real config/locales/en.yml
I test #store_translations from console and it works. but when i put line p I18n.t(translations.key.first)
before ensure
word in helper module, it shows me old translations.
Thanks for any help!
To have a complete isolation from I18n
you may want to switch backends like this:
def with_translations(locale, translations)
original_backend = I18n.backend
I18n.backend = I18n::Backend::KeyValue.new Hash.new, true
I18n.backend.store_translations locale, translations
yield
ensure
I18n.backend = original_backend
end
I've dug into this problem and I think I have a rough answer. The problem seems to be caused by the fact that rails does not load translations in the locale files until the first call to I18n.t
is made.
This is a bit old, but still relevant:
https://groups.google.com/forum/?fromgroups=#!msg/rails-i18n/QFe0GDVRIa0/G7K09NAgqJMJ
We've considered a lot of different approaches to this and in the end our solution to this is to defer the actual translation loading to the latest possible point. So clients (like Rails) would only register translation sources to I18n and the backend would actually lazy-load them when the first translation needs to be looked up.
So basically, the existing translations are only loaded when you make a call to I18n.t
. I discovered this while playing around in the console, when I noticed very strange behaviour:
irb(main):001:0> def with_translations(locale, translations)
irb(main):002:1> I18n.backend.store_translations locale, translations
irb(main):003:1> yield
irb(main):004:1> ensure
irb(main):005:1* I18n.reload!
irb(main):006:1> end
=> nil
irb(main):007:0> I18n.reload!
=> false
irb(main):008:0> with_translations(:en, { :messages => "foo" }) { puts I18n.t :messages }
Messages
=> nil
irb(main):009:0> I18n.t :some_other_string
=> "Some translation"
irb(main):010:0> with_translations(:en, { :messages => "foo" }) { puts I18n.t :messages }
foo
=> nil
What is happening has nothing to do with the ensure
block. We can remove it and the same thing will happen:
irb(main):011:0> I18n.reload!
=> false
irb(main):012:0> def with_translations(locale, translations)
irb(main):013:1> I18n.backend.store_translations locale, translations
irb(main):014:1> yield
irb(main):015:1> end
=> nil
irb(main):016:0> with_translations(:en, { :messages => "foo" }) { puts I18n.t :messages }
Messages
Even without the reload, it still spits out the original string because the translation in the I18n.backend.store_translations
call is being overwritten with the original string when I18n.t
is called. If however you call I18n.t
before the above code, it will work (and leave the translation since there is no ensure
block reloading the translations).
So basically, the answer is to just call I18n.t
on an actual translation string to trigger rails to load the existing translations before overriding the one you want to change:
def with_translations(locale, translations)
I18n.t translations.keys.first # to make sure translations are loaded
I18n.backend.store_translations locale, translations
yield
ensure
I18n.reload!
end
I have only tried this in the console, but I believe it should work for your rspec test as well. If anyone has deeper insights into this issue I'd be eager to hear them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With