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.
After being stuck with a similar issue, I ended up writing a helper like this:
module TranslationSupport
def with_translation(replacement_translation)
I18n.t(".").deep_merge!(replacement_translation)
begin
yield
ensure
I18n.reload!
end
end
end
The shortcut here is initialising the backend first with I18n.t(".")
and then merge in the desired values.
As other answers suggest, alternatively one could initialise the i18n backend then use I18n.backend.store_translations
to update the values. The downside of this approach is one needs to pass the locale as an argument.
This helper can then used it like so:
require "rails_helper"
require "support/translation"
RSpec.describe "testing a view with overridden translations" do
include TranslationSupport
it "works" do
translations = {
homepage: {
index: {
label: "Overridden"
}
}
}
with_translation(translations) do
# Your action
end
# Expectations
end
end
And if you don't want to include the helper each time:
RSpec.configure do |config|
config.include TranslationSupport
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.