faker icon indicating copy to clipboard operation
faker copied to clipboard

I18n::MissingTranslationData: Translation missing: en.faker.company.buzzwords (I18n::MissingTranslationData)

Open rocket-turtle opened this issue 1 year ago • 5 comments

Describe the bug

With my Setup I get this error when I call: Faker::Company.catch_phrase

I18n::MissingTranslationData: Translation missing: en.faker.company.buzzwords (I18n::MissingTranslationData)

    raise exception.respond_to?(:to_exception) ? exception.to_exception : exception
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

i18n-1.14.5/lib/i18n.rb:423:in handle_exception' i18n-1.14.5/lib/i18n.rb:396:in translate_key' i18n-1.14.5/lib/i18n.rb:222:in translate' faker-3.4.2/lib/faker.rb:173:in block in translate' faker-3.4.2/lib/faker.rb:265:in disable_enforce_available_locales' faker-3.4.2/lib/faker.rb:172:in rescue in translate' faker-3.4.2/lib/faker.rb:162:in translate' faker-3.4.2/lib/faker/default/company.rb:57:in catch_phrase'

To Reproduce

Describe a way to reproduce your bug. To get the Faker version, run Faker::VERSION.

Use the reproduction script below to reproduce the issue:

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  git_source(:github) { |repo| "https://github.com/#{repo}.git" }

  gem 'faker', :git => 'https://github.com/faker-ruby/faker.git', :branch => 'main'
  gem "minitest"
end

require "minitest/autorun"
I18n.available_locales = [:de]

class BugTest < Minitest::Test
  def test_faker
    phrase = Faker::Company.catch_phrase
    assert phrase.is_a?(String)
  end
end

Additional context

I18n.enforce_available_locales => true
I18n.locale_available?(:en) => false
I18n.backend.send(:translations).keys => [:de]

Faker has a real simple translation missing fallback:

faker-3.4.2/lib/faker.rb

      # Call I18n.translate with our configured locale if no
      # locale is specified
      def translate(*args, **opts)
        opts[:locale] ||= Faker::Config.locale
        opts[:raise] = true
        I18n.translate(*args, **opts)
      rescue I18n::MissingTranslationData
        opts[:locale] = :en

        # Super-simple fallback -- fallback to en if the
        # translation was missing.  If the translation isn't
        # in en either, then it will raise again.
        disable_enforce_available_locales do
          I18n.translate(*args, **opts)
        end
      end

The disable_enforce_available_locales try to fix, if enforce_available_locales is set but :en is not in the i18n.available_locales.

faker-3.4.2/lib/faker.rb:262

      def disable_enforce_available_locales
        old_enforce_available_locales = I18n.enforce_available_locales
        I18n.enforce_available_locales = false
        yield
      ensure
        I18n.enforce_available_locales = old_enforce_available_locales
      end

Problem

With this setup I18n does not store the :en translation.

i18n-1.14.5/lib/i18n/backend/simple.rb

        def store_translations(locale, data, options = EMPTY_HASH)
          if I18n.enforce_available_locales &&
            I18n.available_locales_initialized? &&
            !I18n.locale_available?(locale)
            return data
          end

Current workaround

Somewhere in my code I make: I18n.reload! if I18n.backend.send(:translations).keys.exclude?(:en)

Solution?

I can not think about a good / easy solution without "polluting" the translations:

Before the reload! I18n.backend.send(:translations).keys.count => 1 After the reload! I18n.backend.send(:translations).keys.count => 66

Maybe the solution is in https://github.com/faker-ruby/faker/issues/2719

The only way I see this becoming faster is to remove i18n and lazy load those yaml files we need.

rocket-turtle avatar Aug 02 '24 09:08 rocket-turtle

Thanks for reporting the issue, @rocket-turtle !

That's interesting. In your setup, I18n has available_locales limited to [:de], but when faker catches the error and tries again passing opts[:locale] = :en, then I18n won't try to load the en locale, as it's not available.

For me, I18n's behavior makes sense, as faker is trying to force a locale that is not even available.

So maybe this line here is not working as expected: https://github.com/faker-ruby/faker/blob/97cfd1a1b0522a16b402e9d14c3248076b24c942/lib/faker.rb#L262

Changing I18n.enforce_available_locales might not really work, or maybe it requires I18n.reload!. I would have to double check what this is supposed to be doing.

--

For now, if the :de locale doesn't have catch phrases available, maybe you could use a different generator instead?

We would also be open to accepting a PR that expands the de.company locale to add catch phrases.

thdaraujo avatar Aug 06 '24 02:08 thdaraujo

I just added :en in my populate.rake and reload all the translations. That "fixes" the problem for my setup.

    I18n.available_locales << :en
    I18n.reload!

If others have the same problem and https://github.com/faker-ruby/faker/issues/2719 is not moving to a different solution for loading the translations I can work on an PR but i think it would not be nice because disable_enforce_available_locales would have to reload the translations twice for every call if :en is not available.

rocket-turtle avatar Aug 12 '24 13:08 rocket-turtle

I came to the same conclusion than @rocket-turtle

I use Faker in a Rails app with FactoryBot in my RSpec code. So one way to circumvent this is to (in config/environments/test.rb):

  • Disable locale enforcing from available locales, in order for Faker to properly load en translations and make them available, when :en is not in the available locals.
  • Be sure to enable raise_on_missing_translations, to catch any missing key elsewhere in the application.

But I'm not satisfied as the translation cache is no VERY polluted with every Faker translations from every languages...

I currently use a fork of Faker that reloads I18n on missing translation (doing it once at the first missing translation, not every time). This way only :en translations are add and are polluting I18n. But still very basic and work is needed to reduce useless :en keys storage (when those keys are present in desired locale).

meuble avatar Aug 19 '24 17:08 meuble

My workaround is:

in the config/environments/test.rb:

Rails.application.configure do
  config.i18n.enforce_available_locales = false
end

and in spec/spec_helper.rb (or spec/rails_helper.rb):

I18n.load_path += Dir[File.join(`bundle show faker`.strip, 'lib', 'locales', '**/*.yml')]
Faker::Name.female_first_name # this line of code is to load the faker paths

I hope we can find a better solution, because disabling enforce_available_locales doesn't seem a good idea.

WaKeMaTTa avatar Dec 04 '24 14:12 WaKeMaTTa

Here's a one-line workaround:

Rails.application.config.i18n.available_locales << :en if Rails.env.test?

I placed it in config/initializers/i18n.rb, but it can probably be placed elsewhere as well.

goulvench avatar May 21 '25 21:05 goulvench