Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ def set_locale
else
SiteSetting.default_locale
end

I18n.fallbacks.ensure_loaded!
end

def store_preloaded(key, json)
Expand Down
5 changes: 0 additions & 5 deletions config/cloud/cloud66/files/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@
# Specifies the header that your server uses for sending files
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx

# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation can not be found)
config.i18n.fallbacks = true


# you may use other configuration here for mail eg: sendgrid

config.action_mailer.delivery_method = :smtp
Expand Down
4 changes: 0 additions & 4 deletions config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@

config.log_level = :info

# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation can not be found)
config.i18n.fallbacks = true

if GlobalSetting.smtp_address
settings = {
address: GlobalSetting.smtp_address,
Expand Down
4 changes: 0 additions & 4 deletions config/environments/profile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@
# Specifies the header that your server uses for sending files
config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx

# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation can not be found)
config.i18n.fallbacks = true

# we recommend you use mailcatcher https://github.com/sj26/mailcatcher
config.action_mailer.smtp_settings = { address: "localhost", port: 1025 }

Expand Down
24 changes: 24 additions & 0 deletions config/initializers/i18n.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# order: after 02-freedom_patches.rb

# Include pluralization module
require 'i18n/backend/pluralization'
I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization)

# Include fallbacks module
require 'i18n/backend/fallbacks'
I18n.backend.class.send(:include, I18n::Backend::Fallbacks)

# Configure custom fallback order
class FallbackLocaleList < Hash
def [](locale)
# user locale, site locale, english
# TODO - this can be extended to be per-language for a better user experience
# (e.g. fallback zh_TW to zh_CN / vice versa)
[locale, SiteSetting.default_locale.to_sym, :en].uniq.compact
end

def ensure_loaded!
self[I18n.locale].each { |l| I18n.ensure_loaded! l }
Comment on lines +17 to +21

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Fallback translations are only preloaded when the controller explicitly calls I18n.fallbacks.ensure_loaded!, so in other contexts (e.g. background jobs, rake tasks, or code using I18n.with_locale with a different locale) fallback locales' YAML files are never loaded, causing valid fallback translations to be missed; by loading the fallback locales whenever the fallback list is computed, fallback behavior will work consistently everywhere. [logic error]

Severity Level: Major ⚠️
- ⚠️ Background jobs using I18n miss translations from fallback locales.
- ⚠️ Services using I18n.with_locale bypass controller fallback preloading.
- ⚠️ Inconsistent translations between web and non-controller code.
Suggested change
[locale, SiteSetting.default_locale.to_sym, :en].uniq.compact
end
def ensure_loaded!
self[I18n.locale].each { |l| I18n.ensure_loaded! l }
locales = [locale, SiteSetting.default_locale.to_sym, :en].uniq.compact
locales.each { |l| I18n.ensure_loaded!(l) }
locales
end
def ensure_loaded!(locale = I18n.locale)
self[locale]
Steps of Reproduction ✅
1. Note the fallback configuration in `config/initializers/i18n.rb:12-23`:
`FallbackLocaleList#[]` only returns `[locale, SiteSetting.default_locale.to_sym,
:en].uniq.compact` without calling `I18n.ensure_loaded!` on any of these locales, and
`ensure_loaded!` explicitly loads fallbacks only for `I18n.locale`.

2. Observe that `ApplicationController#set_locale` in
`app/controllers/application_controller.rb:152-160` is the only place calling
`I18n.fallbacks.ensure_loaded!`, so fallback locales are proactively loaded only for web
requests that pass through this controller filter.

3. Check the lazy-loading implementation in
`lib/freedom_patches/translate_accelerator.rb:45-65`: `I18n.ensure_loaded!(locale)` and
`load_locale` load translations on demand per locale and track them in `@loaded_locales`,
but this is only invoked when some code explicitly calls `I18n.ensure_loaded!(locale)` (or
`I18n.translate` for the current locale), not automatically for fallback locales.

4. In any non-controller context that uses I18n with fallbacks—for example, service/lib
code such as `PostDestroyer.mark_for_deletion` in `lib/post_destroyer.rb:100-109` wrapped
in `I18n.with_locale(SiteSetting.default_locale)` or any background job that calls
`I18n.t` without going through
`ApplicationController#set_locale``I18n::Backend::Fallbacks` will consult
`I18n.fallbacks[locale]`, which currently just returns the list of fallback locales
without loading their YAML files; if a translation key exists only in a fallback locale
(e.g., in `:en` but not in the current locale), lookup will fail because that fallback
locale's files were never loaded in this process. The proposed change makes
`FallbackLocaleList#[]` load each locale via `I18n.ensure_loaded!` whenever the fallback
list is computed, so fallback behavior works consistently even in these non-controller
contexts.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** config/initializers/i18n.rb
**Line:** 17:21
**Comment:**
	*Logic Error: Fallback translations are only preloaded when the controller explicitly calls `I18n.fallbacks.ensure_loaded!`, so in other contexts (e.g. background jobs, rake tasks, or code using `I18n.with_locale` with a different locale) fallback locales' YAML files are never loaded, causing valid fallback translations to be missed; by loading the fallback locales whenever the fallback list is computed, fallback behavior will work consistently everywhere.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

end
end
I18n.fallbacks = FallbackLocaleList.new
2 changes: 0 additions & 2 deletions config/initializers/pluralization.rb

This file was deleted.

5 changes: 5 additions & 0 deletions lib/freedom_patches/translate_accelerator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ def load_locale(locale)
end
end

def ensure_loaded!(locale)
@loaded_locales ||= []
load_locale locale unless @loaded_locales.include?(locale)
end

def translate(key, *args)
load_locale(config.locale) unless @loaded_locales.include?(config.locale)
return translate_no_cache(key, *args) if args.length > 0
Expand Down