在rspec中翻译i18n

时间:2012-10-18 20:16:54

标签: ruby-on-rails internationalization rspec

我想将我的测试与内化隔离开来。我使用rails 3.2.8和rspec 2.11.1

我将此代码添加到spec/support/translations.rb

module I18nHelpers
  def with_translations(locale, translations)
    I18n.backend.store_translations locale, translations
    yield
  ensure
    I18n.reload!
  end

RSpec.configure do |config|
  config.include I18nHelpers
end

然后我测试应用程序助手:

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

但是这个测试属于消息

Failure/Error: assert_select 'span', text: /foo/
     MiniTest::Assertion:
        </foo/> expected but was
        <"Messages">.

'消息'来自我的真实config/locales/en.yml 我从控制台测试#store_translations,它的工作原理。但是当我在帮助模块中的p I18n.t(translations.key.first)字之前放行ensure时,它会显示旧的翻译。

感谢您的帮助!

3 个答案:

答案 0 :(得分:3)

我已经解决了这个问题,我想我有一个粗略的答案。问题似乎是由于rails在第一次调用I18n.t之前没有加载语言环境文件中的翻译这一事实。

这有点旧,但仍然相关:

https://groups.google.com/forum/?fromgroups=#!msg/rails-i18n/QFe0GDVRIa0/G7K09NAgqJMJ

  

我们已经考虑了很多不同的方法,最后我们的解决方案是将实际的翻译加载推迟到最新的可能点。因此,客户端(如Rails)只会将翻译源注册到I18n,而当需要查找第一个翻译时,后端实际上会对它们进行延迟加载。

基本上,只有在拨打I18n.t时才会加载现有翻译。我在控制台玩游戏时发现了这一点,当时我发现了非常奇怪的行为:

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

发生的事情与ensure阻止无关。我们可以删除它,同样的事情会发生:

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

即使没有重新加载,它仍会吐出原始字符串,因为I18n.backend.store_translations调用中的翻译在调用I18n.t时会被原始字符串覆盖。但是,如果您在上面的代码之前调用了I18n.t ,那么它将起作用(并保留翻译,因为没有ensure块重新加载翻译。)

基本上,答案是只需在实际的翻译字符串上调用I18n.t,以触发rails在覆盖您想要更改的字段之前加载现有的翻译

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

我只是在控制台中试过这个,但我相信它也适用于你的rspec测试。如果有人对这个问题有更深入的了解,我会很想听到它们。

答案 1 :(得分:3)

要与I18n完全隔离,您可能需要切换这样的后端:

def with_translations(locale, translations)
  original_backend = I18n.backend

  I18n.backend = I18n::Backend::KeyValue.new Hash.new, true
  I18n.backend.store_translations locale, translations

  yeild
ensure
  I18n.backend = original_backend
end

答案 2 :(得分:0)

在遇到类似问题之后,我最终写了一个这样的帮手:

辅助

module TranslationSupport
  def with_translation(replacement_translation)
    I18n.t(".").deep_merge!(replacement_translation)
    begin
      yield
    ensure
      I18n.reload!
    end
  end
end

此处的快捷方式是首先使用I18n.t(".")初始化后端,然后合并所需的值。

正如其他答案所示,或者可以初始化i18n后端,然后使用I18n.backend.store_translations更新值。这种方法的缺点是需要将区域设置作为参数传递。

用法

这个助手可以像这样使用它:

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

如果你不希望每次都包括帮助者:

RSpec.configure do |config|
  config.include TranslationSupport
end