Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
rocket-turtle opened this issue Aug 2, 2024 · 4 comments

Comments

@rocket-turtle
Copy link

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 #2719

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

@thdaraujo
Copy link
Contributor

thdaraujo commented Aug 6, 2024

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:

def disable_enforce_available_locales

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.

@rocket-turtle
Copy link
Author

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 #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.

@meuble
Copy link
Contributor

meuble commented Aug 19, 2024

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).

@WaKeMaTTa
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants