i18n 0.9.5 → 1.8.8
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 +4 -4
- data/README.md +70 -32
- data/lib/i18n.rb +95 -36
- data/lib/i18n/backend.rb +3 -0
- data/lib/i18n/backend/base.rb +53 -23
- data/lib/i18n/backend/cache.rb +10 -11
- data/lib/i18n/backend/cache_file.rb +36 -0
- data/lib/i18n/backend/cascade.rb +3 -1
- data/lib/i18n/backend/chain.rb +38 -5
- data/lib/i18n/backend/fallbacks.rb +25 -14
- data/lib/i18n/backend/flatten.rb +10 -5
- data/lib/i18n/backend/gettext.rb +4 -0
- data/lib/i18n/backend/interpolation_compiler.rb +3 -1
- data/lib/i18n/backend/key_value.rb +31 -2
- data/lib/i18n/backend/memoize.rb +10 -2
- data/lib/i18n/backend/metadata.rb +5 -3
- data/lib/i18n/backend/pluralization.rb +3 -1
- data/lib/i18n/backend/simple.rb +26 -10
- data/lib/i18n/backend/transliterator.rb +2 -0
- data/lib/i18n/config.rb +20 -2
- data/lib/i18n/core_ext/hash.rb +54 -24
- data/lib/i18n/exceptions.rb +23 -16
- data/lib/i18n/gettext.rb +2 -0
- data/lib/i18n/gettext/helpers.rb +4 -2
- data/lib/i18n/gettext/po_parser.rb +7 -7
- data/lib/i18n/interpolate/ruby.rb +6 -4
- data/lib/i18n/locale.rb +2 -0
- data/lib/i18n/locale/fallbacks.rb +9 -6
- data/lib/i18n/locale/tag/parents.rb +8 -6
- data/lib/i18n/locale/tag/simple.rb +1 -1
- data/lib/i18n/middleware.rb +2 -0
- data/lib/i18n/tests.rb +2 -0
- data/lib/i18n/tests/interpolation.rb +9 -4
- data/lib/i18n/tests/link.rb +11 -1
- data/lib/i18n/tests/localization/date.rb +29 -7
- data/lib/i18n/tests/localization/date_time.rb +27 -6
- data/lib/i18n/tests/localization/procs.rb +6 -5
- data/lib/i18n/tests/localization/time.rb +26 -4
- data/lib/i18n/tests/lookup.rb +2 -2
- data/lib/i18n/tests/procs.rb +5 -0
- data/lib/i18n/version.rb +3 -1
- metadata +14 -60
- data/gemfiles/Gemfile.rails-3.2.x +0 -10
- data/gemfiles/Gemfile.rails-4.0.x +0 -10
- data/gemfiles/Gemfile.rails-4.1.x +0 -10
- data/gemfiles/Gemfile.rails-4.2.x +0 -10
- data/gemfiles/Gemfile.rails-5.0.x +0 -10
- data/gemfiles/Gemfile.rails-5.1.x +0 -10
- data/gemfiles/Gemfile.rails-master +0 -10
- data/lib/i18n/core_ext/kernel/suppress_warnings.rb +0 -8
- data/lib/i18n/core_ext/string/interpolate.rb +0 -9
- data/test/api/all_features_test.rb +0 -58
- data/test/api/cascade_test.rb +0 -28
- data/test/api/chain_test.rb +0 -24
- data/test/api/fallbacks_test.rb +0 -30
- data/test/api/key_value_test.rb +0 -24
- data/test/api/memoize_test.rb +0 -56
- data/test/api/override_test.rb +0 -42
- data/test/api/pluralization_test.rb +0 -30
- data/test/api/simple_test.rb +0 -28
- data/test/backend/cache_test.rb +0 -109
- data/test/backend/cascade_test.rb +0 -86
- data/test/backend/chain_test.rb +0 -122
- data/test/backend/exceptions_test.rb +0 -36
- data/test/backend/fallbacks_test.rb +0 -219
- data/test/backend/interpolation_compiler_test.rb +0 -118
- data/test/backend/key_value_test.rb +0 -61
- data/test/backend/memoize_test.rb +0 -79
- data/test/backend/metadata_test.rb +0 -48
- data/test/backend/pluralization_test.rb +0 -45
- data/test/backend/simple_test.rb +0 -103
- data/test/backend/transliterator_test.rb +0 -84
- data/test/core_ext/hash_test.rb +0 -36
- data/test/gettext/api_test.rb +0 -214
- data/test/gettext/backend_test.rb +0 -92
- data/test/i18n/exceptions_test.rb +0 -117
- data/test/i18n/gettext_plural_keys_test.rb +0 -20
- data/test/i18n/interpolate_test.rb +0 -91
- data/test/i18n/load_path_test.rb +0 -34
- data/test/i18n/middleware_test.rb +0 -24
- data/test/i18n_test.rb +0 -462
- data/test/locale/fallbacks_test.rb +0 -133
- data/test/locale/tag/rfc4646_test.rb +0 -143
- data/test/locale/tag/simple_test.rb +0 -32
- data/test/run_all.rb +0 -20
- data/test/test_data/locales/de.po +0 -82
- data/test/test_data/locales/en.rb +0 -3
- data/test/test_data/locales/en.yml +0 -3
- data/test/test_data/locales/invalid/empty.yml +0 -0
- data/test/test_data/locales/invalid/syntax.yml +0 -4
- data/test/test_data/locales/plurals.rb +0 -113
- data/test/test_helper.rb +0 -61
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d85e42515b239906cad750dc259b33896b1af0f323bb0dfa76e00e25fffc4745
|
|
4
|
+
data.tar.gz: 9f7b02056b8d9efe5e753fa21c4d7e40abc1ac7ff5bb219b350d7424a9d8abc8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 20ec433c6d838fcbb62815541827634e38740ceb02c9e8c2f020a72da6d41316016ff89399fc4baaea23150ae6be020a77c8dc7855d1d1da7a5d72f7a3834b30
|
|
7
|
+
data.tar.gz: 27300973f400a0804fed69ab7e522bf2f3e60718a541245f8d6874c244b9a5aff67dbebfc5c503dc82336b962349e23e4d046ee688e2af03840bd9d7528e2a8b
|
data/README.md
CHANGED
|
@@ -1,45 +1,82 @@
|
|
|
1
1
|
# Ruby I18n
|
|
2
2
|
|
|
3
|
-
[](https://github.com/ruby-i18n/i18n/actions?query=workflow%3ARuby)
|
|
4
4
|
|
|
5
|
-
Ruby
|
|
5
|
+
Ruby internationalization and localization (i18n) solution.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Currently maintained by @radar.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Usage
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
* interpolation of values to translations (Ruby 1.9 compatible syntax)
|
|
13
|
-
* pluralization (CLDR compatible)
|
|
14
|
-
* customizable transliteration to ASCII
|
|
15
|
-
* flexible defaults
|
|
16
|
-
* bulk lookup
|
|
17
|
-
* lambdas as translation data
|
|
18
|
-
* custom key/scope separator
|
|
19
|
-
* custom exception handlers
|
|
20
|
-
* extensible architecture with a swappable backend
|
|
11
|
+
### Rails
|
|
21
12
|
|
|
22
|
-
|
|
13
|
+
You will most commonly use this library within a Rails app.
|
|
14
|
+
|
|
15
|
+
[See the Rails Guide](https://guides.rubyonrails.org/i18n.html) for an example of its usage.
|
|
16
|
+
|
|
17
|
+
### Ruby (without Rails)
|
|
18
|
+
|
|
19
|
+
If you want to use this library without Rails, you can simply add `i18n` to your `Gemfile`:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
gem 'i18n'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then configure I18n with some translations, and a default locale:
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
I18n.load_path << Dir[File.expand_path("config/locales") + "/*.yml"]
|
|
29
|
+
I18n.default_locale = :en # (note that `en` is already the default!)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
A simple translation file in your project might live at `config/locales/en.yml` and look like:
|
|
33
|
+
|
|
34
|
+
```yml
|
|
35
|
+
en:
|
|
36
|
+
test: "This is a test"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
You can then access this translation by doing:
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
I18n.t(:test)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
You can switch locales in your project by setting `I18n.locale` to a different value:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
I18n.locale = :de
|
|
49
|
+
I18n.t(:test) # => "Dies ist ein Test"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
* Translation and localization
|
|
55
|
+
* Interpolation of values to translations
|
|
56
|
+
* Pluralization (CLDR compatible)
|
|
57
|
+
* Customizable transliteration to ASCII
|
|
58
|
+
* Flexible defaults
|
|
59
|
+
* Bulk lookup
|
|
60
|
+
* Lambdas as translation data
|
|
61
|
+
* Custom key/scope separator
|
|
62
|
+
* Custom exception handlers
|
|
63
|
+
* Extensible architecture with a swappable backend
|
|
64
|
+
|
|
65
|
+
## Pluggable Features
|
|
23
66
|
|
|
24
67
|
* Cache
|
|
25
68
|
* Pluralization: lambda pluralizers stored as translation data
|
|
26
69
|
* Locale fallbacks, RFC4647 compliant (optionally: RFC4646 locale validation)
|
|
27
|
-
* [Gettext support](https://github.com/
|
|
70
|
+
* [Gettext support](https://github.com/ruby-i18n/i18n/wiki/Gettext)
|
|
28
71
|
* Translation metadata
|
|
29
72
|
|
|
30
|
-
Alternative
|
|
73
|
+
## Alternative Backend
|
|
31
74
|
|
|
32
75
|
* Chain
|
|
33
76
|
* ActiveRecord (optionally: ActiveRecord::Missing and ActiveRecord::StoreProcs)
|
|
34
77
|
* KeyValue (uses active_support/json and cannot store procs)
|
|
35
78
|
|
|
36
|
-
For more information and lots of resources see [the 'Resources' page on the wiki](https://github.com/
|
|
37
|
-
|
|
38
|
-
## Installation
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
gem install i18n
|
|
42
|
-
```
|
|
79
|
+
For more information and lots of resources see [the 'Resources' page on the wiki](https://github.com/ruby-i18n/i18n/wiki/Resources).
|
|
43
80
|
|
|
44
81
|
## Tests
|
|
45
82
|
|
|
@@ -58,7 +95,7 @@ particular tests in different test cases.
|
|
|
58
95
|
The reason for this is that we need to enforce the I18n API across various
|
|
59
96
|
combinations of extensions. E.g. the Simple backend alone needs to support
|
|
60
97
|
the same API as any combination of feature and/or optimization modules included
|
|
61
|
-
to the Simple backend. We test this by reusing the same API
|
|
98
|
+
to the Simple backend. We test this by reusing the same API definition (implemented
|
|
62
99
|
as test methods) in test cases with different setups.
|
|
63
100
|
|
|
64
101
|
You can find the test cases that enforce the API in test/api. And you can find
|
|
@@ -67,17 +104,18 @@ the API definition test methods in test/api/tests.
|
|
|
67
104
|
All other test cases (e.g. as defined in test/backend, test/core_ext) etc.
|
|
68
105
|
follow the usual test setup and should be easy to grok.
|
|
69
106
|
|
|
70
|
-
##
|
|
107
|
+
## More Documentation
|
|
71
108
|
|
|
72
|
-
|
|
73
|
-
* [Joshua Harvey](http://www.workingwithrails.com/person/759-joshua-harvey)
|
|
74
|
-
* [Stephan Soller](http://www.arkanis-development.de)
|
|
75
|
-
* [Saimon Moore](http://saimonmoore.net)
|
|
76
|
-
* [Matt Aimonetti](https://matt.aimonetti.net/)
|
|
109
|
+
Additional documentation can be found here: https://github.com/ruby-i18n/i18n/wiki
|
|
77
110
|
|
|
78
111
|
## Contributors
|
|
79
112
|
|
|
80
|
-
|
|
113
|
+
* @radar
|
|
114
|
+
* @carlosantoniodasilva
|
|
115
|
+
* @josevalim
|
|
116
|
+
* @knapo
|
|
117
|
+
* @tigrish
|
|
118
|
+
* [and many more](https://github.com/ruby-i18n/i18n/graphs/contributors)
|
|
81
119
|
|
|
82
120
|
## License
|
|
83
121
|
|
data/lib/i18n.rb
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'concurrent/map'
|
|
4
|
+
require 'concurrent/hash'
|
|
2
5
|
|
|
3
6
|
require 'i18n/version'
|
|
4
7
|
require 'i18n/exceptions'
|
|
@@ -12,12 +15,26 @@ module I18n
|
|
|
12
15
|
autoload :Tests, 'i18n/tests'
|
|
13
16
|
autoload :Middleware, 'i18n/middleware'
|
|
14
17
|
|
|
15
|
-
RESERVED_KEYS = [
|
|
18
|
+
RESERVED_KEYS = %i[
|
|
19
|
+
cascade
|
|
20
|
+
deep_interpolation
|
|
21
|
+
default
|
|
22
|
+
exception_handler
|
|
23
|
+
fallback
|
|
24
|
+
fallback_in_progress
|
|
25
|
+
format
|
|
26
|
+
object
|
|
27
|
+
raise
|
|
28
|
+
resolve
|
|
29
|
+
scope
|
|
30
|
+
separator
|
|
31
|
+
throw
|
|
32
|
+
].freeze
|
|
16
33
|
RESERVED_KEYS_PATTERN = /%\{(#{RESERVED_KEYS.join("|")})\}/
|
|
17
|
-
|
|
34
|
+
EMPTY_HASH = {}.freeze
|
|
18
35
|
|
|
19
36
|
def self.new_double_nested_cache # :nodoc:
|
|
20
|
-
Concurrent::Map.new { |h,k| h[k] = Concurrent::Map.new }
|
|
37
|
+
Concurrent::Map.new { |h, k| h[k] = Concurrent::Map.new }
|
|
21
38
|
end
|
|
22
39
|
|
|
23
40
|
module Base
|
|
@@ -53,6 +70,13 @@ module I18n
|
|
|
53
70
|
config.backend.reload!
|
|
54
71
|
end
|
|
55
72
|
|
|
73
|
+
# Tells the backend to load translations now. Used in situations like the
|
|
74
|
+
# Rails production environment. Backends can implement whatever strategy
|
|
75
|
+
# is useful.
|
|
76
|
+
def eager_load!
|
|
77
|
+
config.backend.eager_load!
|
|
78
|
+
end
|
|
79
|
+
|
|
56
80
|
# Translates, pluralizes and interpolates a given key using a given locale,
|
|
57
81
|
# scope, and default, as well as interpolation values.
|
|
58
82
|
#
|
|
@@ -92,7 +116,7 @@ module I18n
|
|
|
92
116
|
# *PLURALIZATION*
|
|
93
117
|
#
|
|
94
118
|
# Translation data can contain pluralized translations. Pluralized translations
|
|
95
|
-
# are arrays of
|
|
119
|
+
# are arrays of singular/plural versions of translations like <tt>['Foo', 'Foos']</tt>.
|
|
96
120
|
#
|
|
97
121
|
# Note that <tt>I18n::Backend::Simple</tt> only supports an algorithm for English
|
|
98
122
|
# pluralization rules. Other algorithms can be supported by custom backends.
|
|
@@ -150,18 +174,32 @@ module I18n
|
|
|
150
174
|
#
|
|
151
175
|
# It is recommended to use/implement lambdas in an "idempotent" way. E.g. when
|
|
152
176
|
# a cache layer is put in front of I18n.translate it will generate a cache key
|
|
153
|
-
# from the argument values passed to #translate.
|
|
177
|
+
# from the argument values passed to #translate. Therefore your lambdas should
|
|
154
178
|
# always return the same translations/values per unique combination of argument
|
|
155
179
|
# values.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
180
|
+
#
|
|
181
|
+
# *Ruby 2.7+ keyword arguments warning*
|
|
182
|
+
#
|
|
183
|
+
# This method uses keyword arguments.
|
|
184
|
+
# There is a breaking change in ruby that produces warning with ruby 2.7 and won't work as expected with ruby 3.0
|
|
185
|
+
# The "hash" parameter must be passed as keyword argument.
|
|
186
|
+
#
|
|
187
|
+
# Good:
|
|
188
|
+
# I18n.t(:salutation, :gender => 'w', :name => 'Smith')
|
|
189
|
+
# I18n.t(:salutation, **{ :gender => 'w', :name => 'Smith' })
|
|
190
|
+
# I18n.t(:salutation, **any_hash)
|
|
191
|
+
#
|
|
192
|
+
# Bad:
|
|
193
|
+
# I18n.t(:salutation, { :gender => 'w', :name => 'Smith' })
|
|
194
|
+
# I18n.t(:salutation, any_hash)
|
|
195
|
+
#
|
|
196
|
+
def translate(key = nil, throw: false, raise: false, locale: nil, **options) # TODO deprecate :raise
|
|
197
|
+
locale ||= config.locale
|
|
198
|
+
raise Disabled.new('t') if locale == false
|
|
163
199
|
enforce_available_locales!(locale)
|
|
164
200
|
|
|
201
|
+
backend = config.backend
|
|
202
|
+
|
|
165
203
|
result = catch(:exception) do
|
|
166
204
|
if key.is_a?(Array)
|
|
167
205
|
key.map { |k| backend.translate(locale, k, options) }
|
|
@@ -169,21 +207,28 @@ module I18n
|
|
|
169
207
|
backend.translate(locale, key, options)
|
|
170
208
|
end
|
|
171
209
|
end
|
|
172
|
-
|
|
210
|
+
|
|
211
|
+
if result.is_a?(MissingTranslation)
|
|
212
|
+
handle_exception((throw && :throw || raise && :raise), result, locale, key, options)
|
|
213
|
+
else
|
|
214
|
+
result
|
|
215
|
+
end
|
|
173
216
|
end
|
|
174
217
|
alias :t :translate
|
|
175
218
|
|
|
176
219
|
# Wrapper for <tt>translate</tt> that adds <tt>:raise => true</tt>. With
|
|
177
220
|
# this option, if no translation is found, it will raise <tt>I18n::MissingTranslationData</tt>
|
|
178
|
-
def translate!(key, options
|
|
179
|
-
translate(key, options
|
|
221
|
+
def translate!(key, **options)
|
|
222
|
+
translate(key, **options, raise: true)
|
|
180
223
|
end
|
|
181
224
|
alias :t! :translate!
|
|
182
225
|
|
|
183
226
|
# Returns true if a translation exists for a given key, otherwise returns false.
|
|
184
|
-
def exists?(key,
|
|
227
|
+
def exists?(key, _locale = nil, locale: _locale, **options)
|
|
228
|
+
locale ||= config.locale
|
|
229
|
+
raise Disabled.new('exists?') if locale == false
|
|
185
230
|
raise I18n::ArgumentError if key.is_a?(String) && key.empty?
|
|
186
|
-
config.backend.exists?(locale, key)
|
|
231
|
+
config.backend.exists?(locale, key, options)
|
|
187
232
|
end
|
|
188
233
|
|
|
189
234
|
# Transliterates UTF-8 characters to ASCII. By default this method will
|
|
@@ -237,37 +282,40 @@ module I18n
|
|
|
237
282
|
# I18n.transliterate("Jürgen") # => "Juergen"
|
|
238
283
|
# I18n.transliterate("Jürgen", :locale => :en) # => "Jurgen"
|
|
239
284
|
# I18n.transliterate("Jürgen", :locale => :de) # => "Juergen"
|
|
240
|
-
def transliterate(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
locale = options && options.delete(:locale) || config.locale
|
|
244
|
-
handling = options && (options.delete(:throw) && :throw || options.delete(:raise) && :raise)
|
|
245
|
-
replacement = options && options.delete(:replacement)
|
|
285
|
+
def transliterate(key, throw: false, raise: false, locale: nil, replacement: nil, **options)
|
|
286
|
+
locale ||= config.locale
|
|
287
|
+
raise Disabled.new('transliterate') if locale == false
|
|
246
288
|
enforce_available_locales!(locale)
|
|
289
|
+
|
|
247
290
|
config.backend.transliterate(locale, key, replacement)
|
|
248
291
|
rescue I18n::ArgumentError => exception
|
|
249
|
-
handle_exception(
|
|
292
|
+
handle_exception((throw && :throw || raise && :raise), exception, locale, key, options)
|
|
250
293
|
end
|
|
251
294
|
|
|
252
295
|
# Localizes certain objects, such as dates and numbers to local formatting.
|
|
253
|
-
def localize(object,
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
format = options.delete(:format) || :default
|
|
296
|
+
def localize(object, locale: nil, format: nil, **options)
|
|
297
|
+
locale ||= config.locale
|
|
298
|
+
raise Disabled.new('l') if locale == false
|
|
257
299
|
enforce_available_locales!(locale)
|
|
300
|
+
|
|
301
|
+
format ||= :default
|
|
258
302
|
config.backend.localize(locale, object, format, options)
|
|
259
303
|
end
|
|
260
304
|
alias :l :localize
|
|
261
305
|
|
|
262
306
|
# Executes block with given I18n.locale set.
|
|
263
307
|
def with_locale(tmp_locale = nil)
|
|
264
|
-
if tmp_locale
|
|
308
|
+
if tmp_locale == nil
|
|
309
|
+
yield
|
|
310
|
+
else
|
|
265
311
|
current_locale = self.locale
|
|
266
|
-
self.locale
|
|
312
|
+
self.locale = tmp_locale
|
|
313
|
+
begin
|
|
314
|
+
yield
|
|
315
|
+
ensure
|
|
316
|
+
self.locale = current_locale
|
|
317
|
+
end
|
|
267
318
|
end
|
|
268
|
-
yield
|
|
269
|
-
ensure
|
|
270
|
-
self.locale = current_locale if tmp_locale
|
|
271
319
|
end
|
|
272
320
|
|
|
273
321
|
# Merges the given locale, key and scope into a single array of keys.
|
|
@@ -291,7 +339,7 @@ module I18n
|
|
|
291
339
|
|
|
292
340
|
# Raises an InvalidLocale exception when the passed locale is not available.
|
|
293
341
|
def enforce_available_locales!(locale)
|
|
294
|
-
if config.enforce_available_locales
|
|
342
|
+
if locale != false && config.enforce_available_locales
|
|
295
343
|
raise I18n::InvalidLocale.new(locale) if !locale_available?(locale)
|
|
296
344
|
end
|
|
297
345
|
end
|
|
@@ -342,11 +390,22 @@ module I18n
|
|
|
342
390
|
@@normalized_key_cache[separator][key] ||=
|
|
343
391
|
case key
|
|
344
392
|
when Array
|
|
345
|
-
key.
|
|
393
|
+
key.flat_map { |k| normalize_key(k, separator) }
|
|
346
394
|
else
|
|
347
395
|
keys = key.to_s.split(separator)
|
|
348
396
|
keys.delete('')
|
|
349
|
-
keys.map!
|
|
397
|
+
keys.map! do |k|
|
|
398
|
+
case k
|
|
399
|
+
when /\A[-+]?[1-9]\d*\z/ # integer
|
|
400
|
+
k.to_i
|
|
401
|
+
when 'true'
|
|
402
|
+
true
|
|
403
|
+
when 'false'
|
|
404
|
+
false
|
|
405
|
+
else
|
|
406
|
+
k.to_sym
|
|
407
|
+
end
|
|
408
|
+
end
|
|
350
409
|
keys
|
|
351
410
|
end
|
|
352
411
|
end
|
data/lib/i18n/backend.rb
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module I18n
|
|
2
4
|
module Backend
|
|
3
5
|
autoload :Base, 'i18n/backend/base'
|
|
4
6
|
autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler'
|
|
5
7
|
autoload :Cache, 'i18n/backend/cache'
|
|
8
|
+
autoload :CacheFile, 'i18n/backend/cache_file'
|
|
6
9
|
autoload :Cascade, 'i18n/backend/cascade'
|
|
7
10
|
autoload :Chain, 'i18n/backend/chain'
|
|
8
11
|
autoload :Fallbacks, 'i18n/backend/fallbacks'
|
data/lib/i18n/backend/base.rb
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'yaml'
|
|
4
|
+
require 'json'
|
|
2
5
|
require 'i18n/core_ext/hash'
|
|
3
|
-
require 'i18n/core_ext/kernel/suppress_warnings'
|
|
4
6
|
|
|
5
7
|
module I18n
|
|
6
8
|
module Backend
|
|
7
9
|
module Base
|
|
10
|
+
using I18n::HashRefinements
|
|
8
11
|
include I18n::Backend::Transliterator
|
|
9
12
|
|
|
10
13
|
# Accepts a list of paths to translation files. Loads translations from
|
|
11
|
-
# plain Ruby (*.rb)
|
|
14
|
+
# plain Ruby (*.rb), YAML files (*.yml), or JSON files (*.json). See #load_rb, #load_yml, and #load_json
|
|
12
15
|
# for details.
|
|
13
16
|
def load_translations(*filenames)
|
|
14
17
|
filenames = I18n.load_path if filenames.empty?
|
|
@@ -17,11 +20,11 @@ module I18n
|
|
|
17
20
|
|
|
18
21
|
# This method receives a locale, a data hash and options for storing translations.
|
|
19
22
|
# Should be implemented
|
|
20
|
-
def store_translations(locale, data, options =
|
|
23
|
+
def store_translations(locale, data, options = EMPTY_HASH)
|
|
21
24
|
raise NotImplementedError
|
|
22
25
|
end
|
|
23
26
|
|
|
24
|
-
def translate(locale, key, options =
|
|
27
|
+
def translate(locale, key, options = EMPTY_HASH)
|
|
25
28
|
raise I18n::ArgumentError if (key.is_a?(String) || key.is_a?(Symbol)) && key.empty?
|
|
26
29
|
raise InvalidLocale.new(locale) unless locale
|
|
27
30
|
return nil if key.nil? && !options.key?(:default)
|
|
@@ -61,14 +64,14 @@ module I18n
|
|
|
61
64
|
entry
|
|
62
65
|
end
|
|
63
66
|
|
|
64
|
-
def exists?(locale, key)
|
|
67
|
+
def exists?(locale, key, options = EMPTY_HASH)
|
|
65
68
|
lookup(locale, key) != nil
|
|
66
69
|
end
|
|
67
70
|
|
|
68
71
|
# Acts the same as +strftime+, but uses a localized version of the
|
|
69
72
|
# format string. Takes a key from the date/time formats translations as
|
|
70
73
|
# a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
|
|
71
|
-
def localize(locale, object, format = :default, options =
|
|
74
|
+
def localize(locale, object, format = :default, options = EMPTY_HASH)
|
|
72
75
|
if object.nil? && options.include?(:default)
|
|
73
76
|
return options[:default]
|
|
74
77
|
end
|
|
@@ -78,7 +81,7 @@ module I18n
|
|
|
78
81
|
key = format
|
|
79
82
|
type = object.respond_to?(:sec) ? 'time' : 'date'
|
|
80
83
|
options = options.merge(:raise => true, :object => object, :locale => locale)
|
|
81
|
-
format = I18n.t(:"#{type}.formats.#{key}", options)
|
|
84
|
+
format = I18n.t(:"#{type}.formats.#{key}", **options)
|
|
82
85
|
end
|
|
83
86
|
|
|
84
87
|
format = translate_localization_format(locale, object, format, options)
|
|
@@ -92,12 +95,21 @@ module I18n
|
|
|
92
95
|
end
|
|
93
96
|
|
|
94
97
|
def reload!
|
|
98
|
+
eager_load! if eager_loaded?
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def eager_load!
|
|
102
|
+
@eager_loaded = true
|
|
95
103
|
end
|
|
96
104
|
|
|
97
105
|
protected
|
|
98
106
|
|
|
107
|
+
def eager_loaded?
|
|
108
|
+
@eager_loaded ||= false
|
|
109
|
+
end
|
|
110
|
+
|
|
99
111
|
# The method which actually looks up for the translation in the store.
|
|
100
|
-
def lookup(locale, key, scope = [], options =
|
|
112
|
+
def lookup(locale, key, scope = [], options = EMPTY_HASH)
|
|
101
113
|
raise NotImplementedError
|
|
102
114
|
end
|
|
103
115
|
|
|
@@ -109,8 +121,8 @@ module I18n
|
|
|
109
121
|
# If given subject is an Array, it walks the array and returns the
|
|
110
122
|
# first translation that can be resolved. Otherwise it tries to resolve
|
|
111
123
|
# the translation directly.
|
|
112
|
-
def default(locale, object, subject, options =
|
|
113
|
-
options = options.
|
|
124
|
+
def default(locale, object, subject, options = EMPTY_HASH)
|
|
125
|
+
options = options.reject { |key, value| key == :default }
|
|
114
126
|
case subject
|
|
115
127
|
when Array
|
|
116
128
|
subject.each do |item|
|
|
@@ -126,15 +138,15 @@ module I18n
|
|
|
126
138
|
# If the given subject is a Symbol, it will be translated with the
|
|
127
139
|
# given options. If it is a Proc then it will be evaluated. All other
|
|
128
140
|
# subjects will be returned directly.
|
|
129
|
-
def resolve(locale, object, subject, options =
|
|
141
|
+
def resolve(locale, object, subject, options = EMPTY_HASH)
|
|
130
142
|
return subject if options[:resolve] == false
|
|
131
143
|
result = catch(:exception) do
|
|
132
144
|
case subject
|
|
133
145
|
when Symbol
|
|
134
|
-
I18n.translate(subject, options.merge(:locale => locale, :throw => true))
|
|
146
|
+
I18n.translate(subject, **options.merge(:locale => locale, :throw => true))
|
|
135
147
|
when Proc
|
|
136
148
|
date_or_time = options.delete(:object) || object
|
|
137
|
-
resolve(locale, object, subject.call(date_or_time, options))
|
|
149
|
+
resolve(locale, object, subject.call(date_or_time, **options))
|
|
138
150
|
else
|
|
139
151
|
subject
|
|
140
152
|
end
|
|
@@ -151,7 +163,8 @@ module I18n
|
|
|
151
163
|
# not standard with regards to the CLDR pluralization rules.
|
|
152
164
|
# Other backends can implement more flexible or complex pluralization rules.
|
|
153
165
|
def pluralize(locale, entry, count)
|
|
154
|
-
|
|
166
|
+
entry = entry.reject { |k, _v| k == :attributes } if entry.is_a?(Hash)
|
|
167
|
+
return entry unless entry.is_a?(Hash) && count && entry.values.none? { |v| v.is_a?(Hash) }
|
|
155
168
|
|
|
156
169
|
key = pluralization_key(entry, count)
|
|
157
170
|
raise InvalidPluralizationData.new(entry, count, key) unless entry.has_key?(key)
|
|
@@ -168,7 +181,7 @@ module I18n
|
|
|
168
181
|
# each element of the array is recursively interpolated (until it finds a string)
|
|
169
182
|
# method interpolates ["yes, %{user}", ["maybe no, %{user}, "no, %{user}"]], :user => "bartuz"
|
|
170
183
|
# # => "["yes, bartuz",["maybe no, bartuz", "no, bartuz"]]"
|
|
171
|
-
def interpolate(locale, subject, values =
|
|
184
|
+
def interpolate(locale, subject, values = EMPTY_HASH)
|
|
172
185
|
return subject if values.empty?
|
|
173
186
|
|
|
174
187
|
case subject
|
|
@@ -184,7 +197,7 @@ module I18n
|
|
|
184
197
|
# deep_interpolate { people: { ann: "Ann is %{ann}", john: "John is %{john}" } },
|
|
185
198
|
# ann: 'good', john: 'big'
|
|
186
199
|
# #=> { people: { ann: "Ann is good", john: "John is big" } }
|
|
187
|
-
def deep_interpolate(locale, data, values =
|
|
200
|
+
def deep_interpolate(locale, data, values = EMPTY_HASH)
|
|
188
201
|
return data if values.empty?
|
|
189
202
|
|
|
190
203
|
case data
|
|
@@ -232,18 +245,35 @@ module I18n
|
|
|
232
245
|
raise InvalidLocaleData.new(filename, e.inspect)
|
|
233
246
|
end
|
|
234
247
|
end
|
|
248
|
+
alias_method :load_yaml, :load_yml
|
|
249
|
+
|
|
250
|
+
# Loads a JSON translations file. The data must have locales as
|
|
251
|
+
# toplevel keys.
|
|
252
|
+
def load_json(filename)
|
|
253
|
+
begin
|
|
254
|
+
::JSON.parse(File.read(filename))
|
|
255
|
+
rescue TypeError, StandardError => e
|
|
256
|
+
raise InvalidLocaleData.new(filename, e.inspect)
|
|
257
|
+
end
|
|
258
|
+
end
|
|
235
259
|
|
|
236
260
|
def translate_localization_format(locale, object, format, options)
|
|
237
|
-
format.to_s.gsub(/%[aAbBpP]/) do |match|
|
|
261
|
+
format.to_s.gsub(/%(|\^)[aAbBpP]/) do |match|
|
|
238
262
|
case match
|
|
239
|
-
when '%a' then I18n.t(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday]
|
|
240
|
-
when '
|
|
241
|
-
when '%
|
|
242
|
-
when '
|
|
243
|
-
when '%
|
|
244
|
-
when '
|
|
263
|
+
when '%a' then I18n.t!(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday]
|
|
264
|
+
when '%^a' then I18n.t!(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday].upcase
|
|
265
|
+
when '%A' then I18n.t!(:"date.day_names", :locale => locale, :format => format)[object.wday]
|
|
266
|
+
when '%^A' then I18n.t!(:"date.day_names", :locale => locale, :format => format)[object.wday].upcase
|
|
267
|
+
when '%b' then I18n.t!(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon]
|
|
268
|
+
when '%^b' then I18n.t!(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon].upcase
|
|
269
|
+
when '%B' then I18n.t!(:"date.month_names", :locale => locale, :format => format)[object.mon]
|
|
270
|
+
when '%^B' then I18n.t!(:"date.month_names", :locale => locale, :format => format)[object.mon].upcase
|
|
271
|
+
when '%p' then I18n.t!(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).upcase if object.respond_to? :hour
|
|
272
|
+
when '%P' then I18n.t!(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).downcase if object.respond_to? :hour
|
|
245
273
|
end
|
|
246
274
|
end
|
|
275
|
+
rescue MissingTranslationData => e
|
|
276
|
+
e.message
|
|
247
277
|
end
|
|
248
278
|
|
|
249
279
|
def pluralization_key(entry, count)
|