i18n 0.9.5 → 1.8.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Build Status](https://
|
3
|
+
[![Build Status](https://github.com/ruby-i18n/i18n/workflows/Ruby/badge.svg)](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)
|