i18n 1.14.7 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cca63854d9dd69dfe1d36eb0388926fc5e7a7cd297ece21e2fdc431ef9c4c286
4
- data.tar.gz: 1ab0f78752eabfd6a03b7c1f4a6f4e45c03689e9fbc0936cd65d6cd1f4103363
3
+ metadata.gz: 765ae65d6898b1a8d8b9766960d44bb1ffd9f97f5455b160cf9e33a94c33803c
4
+ data.tar.gz: ff713e7c2e659e33093223a9e6cb9b95e19ba4a21abb311af4db8fc696185b0e
5
5
  SHA512:
6
- metadata.gz: e8dd4e1cf010b24d748b4e2713794296964301234b9dbed36c96ae591677fb9ca3d5df7f5aedd86a48a1f470ad81de07997f2103107af32786486892204222d4
7
- data.tar.gz: 9e2008459e303dc8e87742231d2468faa3f2edcb61a30c3c26f5240cc5606a8132bccc70ec3e45a9cfcc38eaf04d84c74c6d643ef54f1df2534c66706677180d
6
+ metadata.gz: c5d16537a7975629cb4337f061e5331e76499edf28dfbebcd44f1c5de63423262a95d9d4821366e7e550419aecd5ff92b15be56b50db4b166568b60e93f02642
7
+ data.tar.gz: 290772af479be2b4e345b4434cff6d50a7cdcece0fcb7e9a382c41568f2fbf52c264966e944d3e04fdf27f6d81d51c103f66464c15e2b6bd42e3d60951abc628
data/README.md CHANGED
@@ -19,7 +19,7 @@ We support Rails versions from 6.0 and up.
19
19
 
20
20
  ### Ruby (without Rails)
21
21
 
22
- We support Ruby versions from 3.0 and up.
22
+ We support Ruby versions from 3.2 and up.
23
23
 
24
24
  If you want to use this library without Rails, you can simply add `i18n` to your `Gemfile`:
25
25
 
@@ -54,6 +54,18 @@ I18n.locale = :de
54
54
  I18n.t(:test) # => "Dies ist ein Test"
55
55
  ```
56
56
 
57
+ ### Fiber-safe config updates
58
+
59
+ `Fiber[]` values are inherited by child fibers.
60
+ To avoid sharing mutable config objects across fibers, prefer creating updated configs and setting them with `set!`:
61
+
62
+ ```ruby
63
+ I18n.config.with(locale: :ja).set!
64
+ ```
65
+
66
+ `set!` stores a frozen config in the current execution context.
67
+ This keeps inherited state safe and lets updates happen by replacing config objects.
68
+
57
69
  ## Features
58
70
 
59
71
  * Translation and localization
@@ -16,13 +16,13 @@ module I18n
16
16
  # Returns the current fallbacks implementation. Defaults to +I18n::Locale::Fallbacks+.
17
17
  def fallbacks
18
18
  @@fallbacks ||= I18n::Locale::Fallbacks.new
19
- Thread.current[:i18n_fallbacks] || @@fallbacks
19
+ Fiber[:i18n_fallbacks] || @@fallbacks
20
20
  end
21
21
 
22
22
  # Sets the current fallbacks implementation. Use this to set a different fallbacks implementation.
23
23
  def fallbacks=(fallbacks)
24
24
  @@fallbacks = fallbacks.is_a?(Array) ? I18n::Locale::Fallbacks.new(fallbacks) : fallbacks
25
- Thread.current[:i18n_fallbacks] = @@fallbacks
25
+ Fiber[:i18n_fallbacks] = @@fallbacks
26
26
  end
27
27
  end
28
28
 
@@ -25,6 +25,7 @@ module I18n
25
25
 
26
26
  def compile_if_an_interpolation(string)
27
27
  if interpolated_str?(string)
28
+ string = +string
28
29
  string.instance_eval <<-RUBY_EVAL, __FILE__, __LINE__
29
30
  def i18n_interpolate(v = {})
30
31
  "#{compiled_interpolation_body(string)}"
@@ -42,7 +42,7 @@ module I18n
42
42
  locale = locale.to_sym
43
43
  translations[locale] ||= Concurrent::Hash.new
44
44
  data = Utils.deep_symbolize_keys(data) unless options.fetch(:skip_symbolize_keys, false)
45
- Utils.deep_merge!(translations[locale], data)
45
+ MUTEX.synchronize { Utils.deep_merge!(translations[locale], data) }
46
46
  end
47
47
 
48
48
  # Get available locales from the translations hash
@@ -68,7 +68,8 @@ module I18n
68
68
  "ŧ"=>"t", "Ũ"=>"U", "ũ"=>"u", "Ū"=>"U", "ū"=>"u", "Ŭ"=>"U", "ŭ"=>"u",
69
69
  "Ů"=>"U", "ů"=>"u", "Ű"=>"U", "ű"=>"u", "Ų"=>"U", "ų"=>"u", "Ŵ"=>"W",
70
70
  "ŵ"=>"w", "Ŷ"=>"Y", "ŷ"=>"y", "Ÿ"=>"Y", "Ź"=>"Z", "ź"=>"z", "Ż"=>"Z",
71
- "ż"=>"z", "Ž"=>"Z", "ž"=>"z"
71
+ "ż"=>"z", "Ž"=>"Z", "ž"=>"z", "Ǫ"=>"O", "ǫ"=>"o", "Ǭ"=>"O",
72
+ "ǭ"=>"o"
72
73
  }.freeze
73
74
 
74
75
  def initialize(rule = nil)
@@ -80,7 +81,7 @@ module I18n
80
81
  def transliterate(string, replacement = nil)
81
82
  replacement ||= DEFAULT_REPLACEMENT_CHAR
82
83
  string.gsub(/[^\x00-\x7f]/u) do |char|
83
- approximations[char] || replacement
84
+ approximations[char] || (replacement == :none ? char : replacement)
84
85
  end
85
86
  end
86
87
 
data/lib/i18n/config.rb CHANGED
@@ -10,7 +10,41 @@ module I18n
10
10
  defined?(@locale) && @locale != nil ? @locale : default_locale
11
11
  end
12
12
 
13
- # Sets the current locale pseudo-globally, i.e. in the Thread.current hash.
13
+ def initialize
14
+ @owner = Fiber.current
15
+ end
16
+
17
+ def owned_by?(fiber)
18
+ @owner == fiber
19
+ end
20
+
21
+ def owner=(fiber)
22
+ @owner = fiber
23
+ end
24
+
25
+ def initialize_copy(other)
26
+ @owner = Fiber.current
27
+ end
28
+
29
+ # Returns a copied configuration with the provided attributes set.
30
+ def with(**attrs)
31
+ dup.tap do |copy|
32
+ attrs.each do |name, value|
33
+ copy.public_send("#{name}=", value)
34
+ end
35
+ end
36
+ end
37
+
38
+ # Sets this configuration as the current one for the active execution context.
39
+ # The stored configuration is frozen to avoid sharing mutable state between fibers.
40
+ def set!
41
+ self.owner = Fiber.current unless frozen?
42
+ freeze
43
+ I18n.config = self
44
+ self
45
+ end
46
+
47
+ # Sets the current locale pseudo-globally, i.e. in the Thread.current or Fiber local hash.
14
48
  def locale=(locale)
15
49
  I18n.enforce_available_locales!(locale)
16
50
  @locale = locale && locale.to_sym
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cgi'
4
-
5
3
  module I18n
6
4
  class ExceptionHandler
7
5
  def call(exception, _locale, _key, _options)
@@ -10,7 +10,7 @@ module I18n
10
10
  def call(env)
11
11
  @app.call(env)
12
12
  ensure
13
- Thread.current[:i18n_config] = I18n::Config.new
13
+ Fiber[:i18n_config] = I18n::Config.new
14
14
  end
15
15
 
16
16
  end
@@ -89,14 +89,14 @@ module I18n
89
89
  end
90
90
 
91
91
  test "interpolation: ASCII strings in the backend should be encoded to UTF8 if interpolation options are in UTF8" do
92
- I18n.backend.store_translations 'en', 'encoding' => ('%{who} let me go'.force_encoding("ASCII"))
92
+ I18n.backend.store_translations 'en', 'encoding' => ('%{who} let me go'.dup.force_encoding(Encoding::US_ASCII))
93
93
  result = I18n.t 'encoding', :who => "måmmå miå"
94
94
  assert_equal Encoding::UTF_8, result.encoding
95
95
  end
96
96
 
97
97
  test "interpolation: UTF8 strings in the backend are still returned as UTF8 with ASCII interpolation" do
98
98
  I18n.backend.store_translations 'en', 'encoding' => 'måmmå miå %{what}'
99
- result = I18n.t 'encoding', :what => 'let me go'.force_encoding("ASCII")
99
+ result = I18n.t 'encoding', :what => 'let me go'.dup.force_encoding(Encoding::US_ASCII)
100
100
  assert_equal Encoding::UTF_8, result.encoding
101
101
  end
102
102
 
@@ -172,7 +172,7 @@ module I18n
172
172
  end
173
173
 
174
174
  def euc_jp(string)
175
- string.encode!(Encoding::EUC_JP)
175
+ string.encode(Encoding::EUC_JP)
176
176
  end
177
177
 
178
178
  def interpolate(*args)
data/lib/i18n/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module I18n
4
- VERSION = "1.14.7"
4
+ VERSION = "1.15.0"
5
5
  end
data/lib/i18n.rb CHANGED
@@ -55,12 +55,29 @@ module I18n
55
55
  module Base
56
56
  # Gets I18n configuration object.
57
57
  def config
58
- Thread.current[:i18n_config] ||= I18n::Config.new
58
+ current = Fiber[:i18n_config] || self.config = I18n::Config.new
59
+ if current.respond_to?(:owned_by?) && !current.owned_by?(Fiber.current)
60
+ current = current.dup
61
+ Fiber[:i18n_config] = current
62
+ end
63
+
64
+ current
65
+ end
66
+
67
+ # Gets a mutable I18n configuration object.
68
+ def writable_config
69
+ current = config
70
+ return current unless current.frozen?
71
+
72
+ current = current.dup
73
+ Fiber[:i18n_config] = current
74
+ current
59
75
  end
60
76
 
61
77
  # Sets I18n configuration object.
62
78
  def config=(value)
63
- Thread.current[:i18n_config] = value
79
+ Fiber[:i18n_config] = value
80
+ value.owner = Fiber.current if value.respond_to?(:owner=) && !value.frozen?
64
81
  end
65
82
 
66
83
  # Write methods which delegates to the configuration object
@@ -72,7 +89,7 @@ module I18n
72
89
  end
73
90
 
74
91
  def #{method}=(value)
75
- config.#{method} = (value)
92
+ writable_config.#{method} = (value)
76
93
  end
77
94
  DELEGATORS
78
95
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: i18n
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.14.7
4
+ version: 1.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sven Fuchs
@@ -10,10 +10,9 @@ authors:
10
10
  - Stephan Soller
11
11
  - Saimon Moore
12
12
  - Ryan Bigg
13
- autorequire:
14
13
  bindir: bin
15
14
  cert_chain: []
16
- date: 2025-01-19 00:00:00.000000000 Z
15
+ date: 1980-01-02 00:00:00.000000000 Z
17
16
  dependencies:
18
17
  - !ruby/object:Gem::Dependency
19
18
  name: concurrent-ruby
@@ -91,7 +90,6 @@ metadata:
91
90
  changelog_uri: https://github.com/ruby-i18n/i18n/releases
92
91
  documentation_uri: https://guides.rubyonrails.org/i18n.html
93
92
  source_code_uri: https://github.com/ruby-i18n/i18n
94
- post_install_message:
95
93
  rdoc_options: []
96
94
  require_paths:
97
95
  - lib
@@ -99,15 +97,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
99
97
  requirements:
100
98
  - - ">="
101
99
  - !ruby/object:Gem::Version
102
- version: 2.3.0
100
+ version: '3.1'
103
101
  required_rubygems_version: !ruby/object:Gem::Requirement
104
102
  requirements:
105
103
  - - ">="
106
104
  - !ruby/object:Gem::Version
107
105
  version: 1.3.5
108
106
  requirements: []
109
- rubygems_version: 3.5.23
110
- signing_key:
107
+ rubygems_version: 3.6.9
111
108
  specification_version: 4
112
109
  summary: New wave Internationalization support for Ruby
113
110
  test_files: []