Skip to content

Fix: load :en fallback translations if the I18n backend discarded them [Fix #2987]#3275

Open
augustocbx wants to merge 1 commit into
faker-ruby:mainfrom
augustocbx:fix/2987-en-fallback-translations-not-loaded
Open

Fix: load :en fallback translations if the I18n backend discarded them [Fix #2987]#3275
augustocbx wants to merge 1 commit into
faker-ruby:mainfrom
augustocbx:fix/2987-en-fallback-translations-not-loaded

Conversation

@augustocbx

Copy link
Copy Markdown

Motivation / Background

Fixes #2987

When an application restricts I18n.available_locales to a list that does not include :en (e.g. I18n.available_locales = [:de]), calling a generator whose key is missing in the current locale raises:

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

Root cause: the i18n backend (I18n::Backend::Simple#store_translations) silently discards translations for locales that are not in I18n.available_locales when it initializes. So faker's :en data is never stored, and the :en fallback in Faker::Base.translate fails: wrapping the lookup in disable_enforce_available_locales cannot help, because it only affects the lookup — the data was already dropped at load time. This is the line @thdaraujo suspected wasn't working as expected in the issue discussion.

Solution: when the :en fallback lookup fails and the backend has no faker :en data (I18n.exists?('faker', :en) is false), load faker's :en locale files via I18n.backend.load_translations and retry. Properties of this approach:

  • Loads only the :en files — it does not pull in all ~60 locales the way the I18n.reload! workarounds from the issue thread do.
  • Purely additive — it does not clear or reload the host application's translations (unlike I18n.reload!, which would drop translations an app stored at runtime), and it does not touch I18n.available_locales or enforce_available_locales.
  • Runs at most once — after loading, I18n.exists?('faker', :en) is true, so subsequent calls never hit this path again.
  • No behavior change otherwise — in a normal setup the guard short-circuits immediately, and keys genuinely missing from :en still raise I18n::MissingTranslationData as before (existing tests cover this).

Both I18n.exists? and load_translations are part of the I18n::Backend::Base API and are available in i18n >= 1.8.11 (the gemspec minimum), including the Chain backend.

Additional information

Console output running the reproduction script from the issue against this branch:

Before:

I18n.available_locales = [:de]
Faker::Company.catch_phrase
# => I18n::MissingTranslationData: Translation missing: en.faker.company.buzzwords

After:

I18n.available_locales = [:de]
Faker::Company.catch_phrase
# => "Public-key human-resource access"
I18n.enforce_available_locales  # => true (restored)
I18n.available_locales          # => [:de] (untouched)
I18n.backend.send(:translations).keys  # => [:de, :en] (only :en added, not all locales)
Faker::Base.translate("faker.nonexistent_key")  # => still raises I18n::MissingTranslationData

The regression test reproduces the issue scenario (it fails with I18n::MissingTranslationData: Translation missing: en.faker.company.buzzwords without the fix). Note the pre-existing test_translation_fallback_without_en_in_available_locales did not catch this because it removes :en after the backend has already initialized and stored the :en data; the bug only manifests when the backend initializes while :en is excluded, hence the I18n.reload! in the new test.

Full suite + RuboCop pass locally: bundle exec rake → 2180 tests, 0 failures, 0 errors; 576 files inspected, no offenses.

Checklist

  • This Pull Request is related to one change. Changes that are unrelated should be opened in separate PRs.
  • Commit message has a detailed description of what changed and why. If this PR fixes a related issue include it in the commit message. Ex: [Fix #issue-number]
  • Tests are added or updated if you fix a bug, refactor something, or add a feature.
  • Tests and Rubocop are passing before submitting your proposed changes.

When an application restricts I18n.available_locales to a list that
does not include :en (e.g. `I18n.available_locales = [:de]`), the i18n
backend discards faker's :en translations when it initializes. Faker's
:en fallback in `translate` then raises I18n::MissingTranslationData
even though the key exists in faker's :en locale files: disabling
enforce_available_locales at lookup time cannot restore data that was
never stored.

Now, when the :en fallback lookup fails and the backend has no faker
:en data, load faker's :en locale files into the backend and retry.
This loads only the :en files (not all locales), does not modify
I18n.available_locales or any already-stored translations, and runs at
most once since the data stays stored afterwards. Translations
genuinely missing from :en still raise as before.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant