-
-
Notifications
You must be signed in to change notification settings - Fork 417
Description
What I tried to do
I observed that I18n.locale is unexpectedly reset when entering a new Fiber context, because I18n stores its config using Thread.current[:i18n_config].
In Ruby, Thread.current[:key] is fiber-local, not strictly thread-local, so each Fiber gets its own isolated config.
This behavior may affect Fiber-based environments, including:
- CSV library v3.2.6+ (uses Fibers internally for enumeration)
- Falcon web server (uses Fibers for request handling)
- Code that relies on Enumerator (which internally creates Fibers)
What I expected to happen
I18n.locale should be consistent across Fibers in the same Thread.
[before] I18n.locale=en
[fiber] I18n.locale=en
[after] I18n.locale=enWhat actually happened
When a new Fiber is created, I18n.locale falls back to I18n.default_locale because the config is missing in the new Fiber context.
[before] I18n.locale=en
[fiber] I18n.locale=ja
[after] I18n.locale=enVersions of i18n, rails, and anything else you think is necessary
Ruby version: 3.4.6
I18n version: 1.14.7
Steps to reproduce
require "i18n"
I18n.available_locales = [:en, :ja]
I18n.default_locale = :ja
I18n.locale = :en
puts "[before] I18n.locale=#{I18n.locale}" # => en
Fiber.new do
puts "[fiber] I18n.locale=#{I18n.locale}" # => ja (unexpected!)
end.resume
puts "[after] I18n.locale=#{I18n.locale}" # => enRoot cause analysis
In lib/i18n.rb, the config method uses fiber-local storage:
def config
Thread.current[:i18n_config] ||= I18n::Config.new
endHowever, Thread.current[:key] is fiber-local in Ruby.
Each Fiber has its own Thread.current hash, so :i18n_config can be missing/reset when code runs inside a Fiber, causing I18n.locale to fall back to default_locale.
Real-world example
This breaks behavior in the CSV library (v3.2.6+):
require "csv"
require "i18n"
I18n.locale = :en
CSV.parse("name\nTaro\n", headers: true, header_converters: [
->(f, info) { puts I18n.locale } # => :ja (wrong!)
])This happens because CSV internally uses Enumerator#next, which creates Fibers.
References
- Related CSV issue: ruby/csv#acc05116