i18n 0.9.5 → 1.6.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 +4 -4
- data/README.md +54 -13
- data/lib/i18n/backend/base.rb +46 -17
- data/lib/i18n/backend/cache.rb +8 -9
- data/lib/i18n/backend/cache_file.rb +36 -0
- data/lib/i18n/backend/cascade.rb +3 -1
- data/lib/i18n/backend/chain.rb +33 -3
- data/lib/i18n/backend/fallbacks.rb +11 -13
- data/lib/i18n/backend/flatten.rb +2 -0
- 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 +2 -0
- data/lib/i18n/backend/simple.rb +26 -8
- data/lib/i18n/backend/transliterator.rb +2 -0
- data/lib/i18n/backend.rb +3 -0
- data/lib/i18n/config.rb +20 -2
- data/lib/i18n/core_ext/hash.rb +42 -24
- data/lib/i18n/exceptions.rb +23 -16
- data/lib/i18n/gettext/helpers.rb +3 -1
- data/lib/i18n/gettext/po_parser.rb +7 -7
- data/lib/i18n/gettext.rb +2 -0
- data/lib/i18n/interpolate/ruby.rb +5 -3
- data/lib/i18n/locale/fallbacks.rb +1 -1
- data/lib/i18n/locale.rb +2 -0
- data/lib/i18n/middleware.rb +2 -0
- data/lib/i18n/tests/interpolation.rb +5 -4
- data/lib/i18n/tests/localization/date.rb +28 -6
- data/lib/i18n/tests/localization/date_time.rb +27 -6
- data/lib/i18n/tests/localization/time.rb +26 -4
- data/lib/i18n/tests.rb +2 -0
- data/lib/i18n/version.rb +3 -1
- data/lib/i18n.rb +72 -30
- metadata +24 -58
- 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: 2b03b84d726d3cafa7ea2ecaa6f258cb911696c338cb32c769f71b6d62ca5fb5
|
4
|
+
data.tar.gz: 7a489bf447e9fedee8fd043cc8ba324c0afc0c34701fbfeff5f15b1c1f4fa6b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 534659c40a8df9eb3fd9a123b779de0f1b66471ecd3fb35e2083128193f51aeea95df6aad06039b6309932a6efb3426c525a8759e70c1104754fdd921732d6cd
|
7
|
+
data.tar.gz: caddebe447a797be3dde1607e74d826588ee13ee1317c6abfbb08cbd8a22d79a6aa10a84d3fd21fa45ca9348317aa0e0aafe1c408d4e1c989fbee7f09cd87606
|
data/README.md
CHANGED
@@ -1,12 +1,55 @@
|
|
1
1
|
# Ruby I18n
|
2
2
|
|
3
|
-
[](https://travis-ci.org/ruby-i18n/i18n)
|
4
4
|
|
5
5
|
Ruby Internationalization and localization solution.
|
6
6
|
|
7
|
-
|
7
|
+
Currently maintained by @radar.
|
8
8
|
|
9
|
-
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
### Rails
|
12
|
+
|
13
|
+
You will most commonly use this library within a Rails app.
|
14
|
+
|
15
|
+
[See the Rails Guide](http://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
|
10
53
|
|
11
54
|
* translation and localization
|
12
55
|
* interpolation of values to translations (Ruby 1.9 compatible syntax)
|
@@ -19,27 +62,21 @@ Features:
|
|
19
62
|
* custom exception handlers
|
20
63
|
* extensible architecture with a swappable backend
|
21
64
|
|
22
|
-
Pluggable
|
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
|
|
@@ -67,6 +104,10 @@ 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
|
|
107
|
+
## More Documentation
|
108
|
+
|
109
|
+
Additional documentation can be found here: https://github.com/svenfuchs/i18n/wiki
|
110
|
+
|
70
111
|
## Authors
|
71
112
|
|
72
113
|
* [Sven Fuchs](http://www.artweb-design.de)
|
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)
|
@@ -68,7 +71,7 @@ module I18n
|
|
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
|
@@ -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,7 +121,7 @@ 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 =
|
124
|
+
def default(locale, object, subject, options = EMPTY_HASH)
|
113
125
|
options = options.dup.reject { |key, value| key == :default }
|
114
126
|
case subject
|
115
127
|
when Array
|
@@ -126,7 +138,7 @@ 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
|
@@ -168,7 +180,7 @@ module I18n
|
|
168
180
|
# each element of the array is recursively interpolated (until it finds a string)
|
169
181
|
# method interpolates ["yes, %{user}", ["maybe no, %{user}, "no, %{user}"]], :user => "bartuz"
|
170
182
|
# # => "["yes, bartuz",["maybe no, bartuz", "no, bartuz"]]"
|
171
|
-
def interpolate(locale, subject, values =
|
183
|
+
def interpolate(locale, subject, values = EMPTY_HASH)
|
172
184
|
return subject if values.empty?
|
173
185
|
|
174
186
|
case subject
|
@@ -184,7 +196,7 @@ module I18n
|
|
184
196
|
# deep_interpolate { people: { ann: "Ann is %{ann}", john: "John is %{john}" } },
|
185
197
|
# ann: 'good', john: 'big'
|
186
198
|
# #=> { people: { ann: "Ann is good", john: "John is big" } }
|
187
|
-
def deep_interpolate(locale, data, values =
|
199
|
+
def deep_interpolate(locale, data, values = EMPTY_HASH)
|
188
200
|
return data if values.empty?
|
189
201
|
|
190
202
|
case data
|
@@ -232,18 +244,35 @@ module I18n
|
|
232
244
|
raise InvalidLocaleData.new(filename, e.inspect)
|
233
245
|
end
|
234
246
|
end
|
247
|
+
alias_method :load_yaml, :load_yml
|
248
|
+
|
249
|
+
# Loads a JSON translations file. The data must have locales as
|
250
|
+
# toplevel keys.
|
251
|
+
def load_json(filename)
|
252
|
+
begin
|
253
|
+
::JSON.parse(File.read(filename))
|
254
|
+
rescue TypeError, StandardError => e
|
255
|
+
raise InvalidLocaleData.new(filename, e.inspect)
|
256
|
+
end
|
257
|
+
end
|
235
258
|
|
236
259
|
def translate_localization_format(locale, object, format, options)
|
237
|
-
format.to_s.gsub(/%[aAbBpP]/) do |match|
|
260
|
+
format.to_s.gsub(/%(|\^)[aAbBpP]/) do |match|
|
238
261
|
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 '
|
262
|
+
when '%a' then I18n.t!(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday]
|
263
|
+
when '%^a' then I18n.t!(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday].upcase
|
264
|
+
when '%A' then I18n.t!(:"date.day_names", :locale => locale, :format => format)[object.wday]
|
265
|
+
when '%^A' then I18n.t!(:"date.day_names", :locale => locale, :format => format)[object.wday].upcase
|
266
|
+
when '%b' then I18n.t!(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon]
|
267
|
+
when '%^b' then I18n.t!(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon].upcase
|
268
|
+
when '%B' then I18n.t!(:"date.month_names", :locale => locale, :format => format)[object.mon]
|
269
|
+
when '%^B' then I18n.t!(:"date.month_names", :locale => locale, :format => format)[object.mon].upcase
|
270
|
+
when '%p' then I18n.t!(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).upcase if object.respond_to? :hour
|
271
|
+
when '%P' then I18n.t!(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).downcase if object.respond_to? :hour
|
245
272
|
end
|
246
273
|
end
|
274
|
+
rescue MissingTranslationData => e
|
275
|
+
e.message
|
247
276
|
end
|
248
277
|
|
249
278
|
def pluralization_key(entry, count)
|
data/lib/i18n/backend/cache.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This module allows you to easily cache all responses from the backend - thus
|
2
4
|
# speeding up the I18n aspects of your application quite a bit.
|
3
5
|
#
|
@@ -14,9 +16,9 @@
|
|
14
16
|
# ActiveSupport::Cache (only the methods #fetch and #write are being used).
|
15
17
|
#
|
16
18
|
# The cache_key implementation by default assumes you pass values that return
|
17
|
-
# a valid key from #hash (see
|
18
|
-
# http://www.ruby-doc.org/core/classes/Object.html#M000337). However, you can
|
19
|
-
# configure your own digest method via which responds to #hexdigest (see
|
19
|
+
# a valid key from #hash (see
|
20
|
+
# http://www.ruby-doc.org/core/classes/Object.html#M000337). However, you can
|
21
|
+
# configure your own digest method via which responds to #hexdigest (see
|
20
22
|
# http://ruby-doc.org/stdlib/libdoc/digest/rdoc/index.html):
|
21
23
|
#
|
22
24
|
# I18n.cache_key_digest = Digest::MD5.new
|
@@ -75,7 +77,7 @@ module I18n
|
|
75
77
|
module Backend
|
76
78
|
# TODO Should the cache be cleared if new translations are stored?
|
77
79
|
module Cache
|
78
|
-
def translate(locale, key, options =
|
80
|
+
def translate(locale, key, options = EMPTY_HASH)
|
79
81
|
I18n.perform_caching? ? fetch(cache_key(locale, key, options)) { super } : super
|
80
82
|
end
|
81
83
|
|
@@ -98,16 +100,13 @@ module I18n
|
|
98
100
|
|
99
101
|
def cache_key(locale, key, options)
|
100
102
|
# This assumes that only simple, native Ruby values are passed to I18n.translate.
|
101
|
-
"i18n/#{I18n.cache_namespace}/#{locale}/#{digest_item(key)}/#{
|
103
|
+
"i18n/#{I18n.cache_namespace}/#{locale}/#{digest_item(key)}/#{digest_item(options)}"
|
102
104
|
end
|
103
105
|
|
104
106
|
private
|
105
|
-
# In Ruby < 1.9 the following is true: { :foo => 1, :bar => 2 }.hash == { :foo => 2, :bar => 1 }.hash
|
106
|
-
# Therefore we must use the hash of the inspect string instead to avoid cache key colisions.
|
107
|
-
USE_INSPECT_HASH = RUBY_VERSION <= "1.9"
|
108
107
|
|
109
108
|
def digest_item(key)
|
110
|
-
I18n.cache_key_digest ? I18n.cache_key_digest.hexdigest(key.to_s) : key.hash
|
109
|
+
I18n.cache_key_digest ? I18n.cache_key_digest.hexdigest(key.to_s) : key.to_s.hash
|
111
110
|
end
|
112
111
|
end
|
113
112
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest/sha2'
|
4
|
+
|
5
|
+
module I18n
|
6
|
+
module Backend
|
7
|
+
# Overwrites the Base load_file method to cache loaded file contents.
|
8
|
+
module CacheFile
|
9
|
+
# Optionally provide path_roots array to normalize filename paths,
|
10
|
+
# to make the cached i18n data portable across environments.
|
11
|
+
attr_accessor :path_roots
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
# Track loaded translation files in the `i18n.load_file` scope,
|
16
|
+
# and skip loading the file if its contents are still up-to-date.
|
17
|
+
def load_file(filename)
|
18
|
+
initialized = !respond_to?(:initialized?) || initialized?
|
19
|
+
key = I18n::Backend::Flatten.escape_default_separator(normalized_path(filename))
|
20
|
+
old_mtime, old_digest = initialized && lookup(:i18n, key, :load_file)
|
21
|
+
return if (mtime = File.mtime(filename).to_i) == old_mtime ||
|
22
|
+
(digest = Digest::SHA2.file(filename).hexdigest) == old_digest
|
23
|
+
super
|
24
|
+
store_translations(:i18n, load_file: { key => [mtime, digest] })
|
25
|
+
end
|
26
|
+
|
27
|
+
# Translate absolute filename to relative path for i18n key.
|
28
|
+
def normalized_path(file)
|
29
|
+
return file unless path_roots
|
30
|
+
path = path_roots.find(&file.method(:start_with?)) ||
|
31
|
+
raise(InvalidLocaleData.new(file, 'outside expected path roots'))
|
32
|
+
file.sub(path, path_roots.index(path).to_s)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/i18n/backend/cascade.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# The Cascade module adds the ability to do cascading lookups to backends that
|
2
4
|
# are compatible to the Simple backend.
|
3
5
|
#
|
@@ -31,7 +33,7 @@
|
|
31
33
|
module I18n
|
32
34
|
module Backend
|
33
35
|
module Cascade
|
34
|
-
def lookup(locale, key, scope = [], options =
|
36
|
+
def lookup(locale, key, scope = [], options = EMPTY_HASH)
|
35
37
|
return super unless cascade = options[:cascade]
|
36
38
|
|
37
39
|
cascade = { :step => 1 } unless cascade.is_a?(Hash)
|
data/lib/i18n/backend/chain.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module I18n
|
2
4
|
module Backend
|
3
5
|
# Backend that chains multiple other backends and checks each of them when
|
@@ -15,6 +17,8 @@ module I18n
|
|
15
17
|
# The implementation assumes that all backends added to the Chain implement
|
16
18
|
# a lookup method with the same API as Simple backend does.
|
17
19
|
class Chain
|
20
|
+
using I18n::HashRefinements
|
21
|
+
|
18
22
|
module Implementation
|
19
23
|
include Base
|
20
24
|
|
@@ -24,11 +28,24 @@ module I18n
|
|
24
28
|
self.backends = backends
|
25
29
|
end
|
26
30
|
|
31
|
+
def initialized?
|
32
|
+
backends.all? do |backend|
|
33
|
+
backend.instance_eval do
|
34
|
+
return false unless initialized?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
27
40
|
def reload!
|
28
41
|
backends.each { |backend| backend.reload! }
|
29
42
|
end
|
30
43
|
|
31
|
-
def
|
44
|
+
def eager_load!
|
45
|
+
backends.each { |backend| backend.eager_load! }
|
46
|
+
end
|
47
|
+
|
48
|
+
def store_translations(locale, data, options = EMPTY_HASH)
|
32
49
|
backends.first.store_translations(locale, data, options)
|
33
50
|
end
|
34
51
|
|
@@ -36,7 +53,7 @@ module I18n
|
|
36
53
|
backends.map { |backend| backend.available_locales }.flatten.uniq
|
37
54
|
end
|
38
55
|
|
39
|
-
def translate(locale, key, default_options =
|
56
|
+
def translate(locale, key, default_options = EMPTY_HASH)
|
40
57
|
namespace = nil
|
41
58
|
options = default_options.except(:default)
|
42
59
|
|
@@ -62,7 +79,7 @@ module I18n
|
|
62
79
|
end
|
63
80
|
end
|
64
81
|
|
65
|
-
def localize(locale, object, format = :default, options =
|
82
|
+
def localize(locale, object, format = :default, options = EMPTY_HASH)
|
66
83
|
backends.each do |backend|
|
67
84
|
catch(:exception) do
|
68
85
|
result = backend.localize(locale, object, format, options) and return result
|
@@ -72,6 +89,19 @@ module I18n
|
|
72
89
|
end
|
73
90
|
|
74
91
|
protected
|
92
|
+
def init_translations
|
93
|
+
backends.each do |backend|
|
94
|
+
backend.send(:init_translations)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def translations
|
99
|
+
backends.first.instance_eval do
|
100
|
+
init_translations unless initialized?
|
101
|
+
translations
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
75
105
|
def namespace_lookup?(result, options)
|
76
106
|
result.is_a?(Hash) && !options.has_key?(:count)
|
77
107
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# I18n locale fallbacks are useful when you want your application to use
|
2
4
|
# translations from other locales when translations for the current locale are
|
3
5
|
# missing. E.g. you might want to use :en translations when translations in
|
@@ -34,25 +36,21 @@ module I18n
|
|
34
36
|
# The default option takes precedence over fallback locales only when
|
35
37
|
# it's a Symbol. When the default contains a String, Proc or Hash
|
36
38
|
# it is evaluated last after all the fallback locales have been tried.
|
37
|
-
def translate(locale, key, options =
|
39
|
+
def translate(locale, key, options = EMPTY_HASH)
|
38
40
|
return super unless options.fetch(:fallback, true)
|
39
41
|
return super if options[:fallback_in_progress]
|
40
42
|
default = extract_non_symbol_default!(options) if options[:default]
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
return result unless result.nil?
|
49
|
-
end
|
50
|
-
rescue I18n::InvalidLocale
|
51
|
-
# we do nothing when the locale is invalid, as this is a fallback anyways.
|
44
|
+
fallback_options = options.merge(:fallback_in_progress => true)
|
45
|
+
I18n.fallbacks[locale].each do |fallback|
|
46
|
+
begin
|
47
|
+
catch(:exception) do
|
48
|
+
result = super(fallback, key, fallback_options)
|
49
|
+
return result unless result.nil?
|
52
50
|
end
|
51
|
+
rescue I18n::InvalidLocale
|
52
|
+
# we do nothing when the locale is invalid, as this is a fallback anyways.
|
53
53
|
end
|
54
|
-
ensure
|
55
|
-
options.delete(:fallback_in_progress)
|
56
54
|
end
|
57
55
|
|
58
56
|
return if options.key?(:default) && options[:default].nil?
|
data/lib/i18n/backend/flatten.rb
CHANGED
data/lib/i18n/backend/gettext.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'i18n/gettext'
|
2
4
|
require 'i18n/gettext/po_parser'
|
3
5
|
|
@@ -29,6 +31,8 @@ module I18n
|
|
29
31
|
# Without it strings containing periods (".") will not be translated.
|
30
32
|
|
31
33
|
module Gettext
|
34
|
+
using I18n::HashRefinements
|
35
|
+
|
32
36
|
class PoData < Hash
|
33
37
|
def set_comment(msgid_or_sym, comment)
|
34
38
|
# ignore
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# The InterpolationCompiler module contains optimizations that can tremendously
|
2
4
|
# speed up the interpolation process on the Simple backend.
|
3
5
|
#
|
@@ -104,7 +106,7 @@ module I18n
|
|
104
106
|
end
|
105
107
|
end
|
106
108
|
|
107
|
-
def store_translations(locale, data, options =
|
109
|
+
def store_translations(locale, data, options = EMPTY_HASH)
|
108
110
|
compile_all_strings_in(data)
|
109
111
|
super
|
110
112
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'i18n/backend/base'
|
2
4
|
|
3
5
|
module I18n
|
@@ -65,6 +67,8 @@ module I18n
|
|
65
67
|
#
|
66
68
|
# This is useful if you are using a KeyValue backend chained to a Simple backend.
|
67
69
|
class KeyValue
|
70
|
+
using I18n::HashRefinements
|
71
|
+
|
68
72
|
module Implementation
|
69
73
|
attr_accessor :store
|
70
74
|
|
@@ -74,7 +78,11 @@ module I18n
|
|
74
78
|
@store, @subtrees = store, subtrees
|
75
79
|
end
|
76
80
|
|
77
|
-
def
|
81
|
+
def initialized?
|
82
|
+
!@store.nil?
|
83
|
+
end
|
84
|
+
|
85
|
+
def store_translations(locale, data, options = EMPTY_HASH)
|
78
86
|
escape = options.fetch(:escape, true)
|
79
87
|
flatten_translations(locale, data, escape, @subtrees).each do |key, value|
|
80
88
|
key = "#{locale}.#{key}"
|
@@ -103,11 +111,31 @@ module I18n
|
|
103
111
|
|
104
112
|
protected
|
105
113
|
|
114
|
+
# Queries the translations from the key-value store and converts
|
115
|
+
# them into a hash such as the one returned from loading the
|
116
|
+
# haml files
|
117
|
+
def translations
|
118
|
+
@translations = @store.keys.clone.map do |main_key|
|
119
|
+
main_value = JSON.decode(@store[main_key])
|
120
|
+
main_key.to_s.split(".").reverse.inject(main_value) do |value, key|
|
121
|
+
{key.to_sym => value}
|
122
|
+
end
|
123
|
+
end.inject{|hash, elem| hash.deep_merge!(elem)}.deep_symbolize_keys
|
124
|
+
end
|
125
|
+
|
126
|
+
def init_translations
|
127
|
+
# NO OP
|
128
|
+
# This call made also inside Simple Backend and accessed by
|
129
|
+
# other plugins like I18n-js and babilu and
|
130
|
+
# to use it along with the Chain backend we need to
|
131
|
+
# provide a uniform API even for protected methods :S
|
132
|
+
end
|
133
|
+
|
106
134
|
def subtrees?
|
107
135
|
@subtrees
|
108
136
|
end
|
109
137
|
|
110
|
-
def lookup(locale, key, scope = [], options =
|
138
|
+
def lookup(locale, key, scope = [], options = EMPTY_HASH)
|
111
139
|
key = normalize_flat_keys(locale, key, scope, options[:separator])
|
112
140
|
value = @store["#{locale}.#{key}"]
|
113
141
|
value = JSON.decode(value) if value
|
@@ -125,6 +153,7 @@ module I18n
|
|
125
153
|
if subtrees?
|
126
154
|
super
|
127
155
|
else
|
156
|
+
return entry unless entry.is_a?(Hash)
|
128
157
|
key = pluralization_key(entry, count)
|
129
158
|
entry[key]
|
130
159
|
end
|
data/lib/i18n/backend/memoize.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Memoize module simply memoizes the values returned by lookup using
|
2
4
|
# a flat hash and can tremendously speed up the lookup process in a backend.
|
3
5
|
#
|
@@ -14,7 +16,7 @@ module I18n
|
|
14
16
|
@memoized_locales ||= super
|
15
17
|
end
|
16
18
|
|
17
|
-
def store_translations(locale, data, options =
|
19
|
+
def store_translations(locale, data, options = EMPTY_HASH)
|
18
20
|
reset_memoizations!(locale)
|
19
21
|
super
|
20
22
|
end
|
@@ -24,9 +26,15 @@ module I18n
|
|
24
26
|
super
|
25
27
|
end
|
26
28
|
|
29
|
+
def eager_load!
|
30
|
+
memoized_lookup
|
31
|
+
available_locales
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
27
35
|
protected
|
28
36
|
|
29
|
-
def lookup(locale, key, scope = nil, options =
|
37
|
+
def lookup(locale, key, scope = nil, options = EMPTY_HASH)
|
30
38
|
flat_key = I18n::Backend::Flatten.normalize_flat_keys(locale,
|
31
39
|
key, scope, options[:separator]).to_sym
|
32
40
|
flat_hash = memoized_lookup[locale.to_sym]
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# I18n translation metadata is useful when you want to access information
|
2
4
|
# about how a translation was looked up, pluralized or interpolated in
|
3
5
|
# your application.
|
@@ -35,19 +37,19 @@ module I18n
|
|
35
37
|
end
|
36
38
|
end
|
37
39
|
|
38
|
-
def translate(locale, key, options =
|
40
|
+
def translate(locale, key, options = EMPTY_HASH)
|
39
41
|
metadata = {
|
40
42
|
:locale => locale,
|
41
43
|
:key => key,
|
42
44
|
:scope => options[:scope],
|
43
45
|
:default => options[:default],
|
44
46
|
:separator => options[:separator],
|
45
|
-
:values => options.reject { |name,
|
47
|
+
:values => options.reject { |name, _value| RESERVED_KEYS.include?(name) }
|
46
48
|
}
|
47
49
|
with_metadata(metadata) { super }
|
48
50
|
end
|
49
51
|
|
50
|
-
def interpolate(locale, entry, values =
|
52
|
+
def interpolate(locale, entry, values = EMPTY_HASH)
|
51
53
|
metadata = entry.translation_metadata.merge(:original => entry)
|
52
54
|
with_metadata(metadata) { super }
|
53
55
|
end
|