i18n 0.9.5 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +54 -13
  3. data/lib/i18n/backend/base.rb +46 -17
  4. data/lib/i18n/backend/cache.rb +8 -9
  5. data/lib/i18n/backend/cache_file.rb +36 -0
  6. data/lib/i18n/backend/cascade.rb +3 -1
  7. data/lib/i18n/backend/chain.rb +33 -3
  8. data/lib/i18n/backend/fallbacks.rb +11 -13
  9. data/lib/i18n/backend/flatten.rb +2 -0
  10. data/lib/i18n/backend/gettext.rb +4 -0
  11. data/lib/i18n/backend/interpolation_compiler.rb +3 -1
  12. data/lib/i18n/backend/key_value.rb +31 -2
  13. data/lib/i18n/backend/memoize.rb +10 -2
  14. data/lib/i18n/backend/metadata.rb +5 -3
  15. data/lib/i18n/backend/pluralization.rb +2 -0
  16. data/lib/i18n/backend/simple.rb +26 -8
  17. data/lib/i18n/backend/transliterator.rb +2 -0
  18. data/lib/i18n/backend.rb +3 -0
  19. data/lib/i18n/config.rb +20 -2
  20. data/lib/i18n/core_ext/hash.rb +42 -24
  21. data/lib/i18n/exceptions.rb +23 -16
  22. data/lib/i18n/gettext/helpers.rb +3 -1
  23. data/lib/i18n/gettext/po_parser.rb +7 -7
  24. data/lib/i18n/gettext.rb +2 -0
  25. data/lib/i18n/interpolate/ruby.rb +5 -3
  26. data/lib/i18n/locale/fallbacks.rb +1 -1
  27. data/lib/i18n/locale.rb +2 -0
  28. data/lib/i18n/middleware.rb +2 -0
  29. data/lib/i18n/tests/interpolation.rb +5 -4
  30. data/lib/i18n/tests/localization/date.rb +28 -6
  31. data/lib/i18n/tests/localization/date_time.rb +27 -6
  32. data/lib/i18n/tests/localization/time.rb +26 -4
  33. data/lib/i18n/tests.rb +2 -0
  34. data/lib/i18n/version.rb +3 -1
  35. data/lib/i18n.rb +72 -30
  36. metadata +24 -58
  37. data/gemfiles/Gemfile.rails-3.2.x +0 -10
  38. data/gemfiles/Gemfile.rails-4.0.x +0 -10
  39. data/gemfiles/Gemfile.rails-4.1.x +0 -10
  40. data/gemfiles/Gemfile.rails-4.2.x +0 -10
  41. data/gemfiles/Gemfile.rails-5.0.x +0 -10
  42. data/gemfiles/Gemfile.rails-5.1.x +0 -10
  43. data/gemfiles/Gemfile.rails-master +0 -10
  44. data/lib/i18n/core_ext/kernel/suppress_warnings.rb +0 -8
  45. data/lib/i18n/core_ext/string/interpolate.rb +0 -9
  46. data/test/api/all_features_test.rb +0 -58
  47. data/test/api/cascade_test.rb +0 -28
  48. data/test/api/chain_test.rb +0 -24
  49. data/test/api/fallbacks_test.rb +0 -30
  50. data/test/api/key_value_test.rb +0 -24
  51. data/test/api/memoize_test.rb +0 -56
  52. data/test/api/override_test.rb +0 -42
  53. data/test/api/pluralization_test.rb +0 -30
  54. data/test/api/simple_test.rb +0 -28
  55. data/test/backend/cache_test.rb +0 -109
  56. data/test/backend/cascade_test.rb +0 -86
  57. data/test/backend/chain_test.rb +0 -122
  58. data/test/backend/exceptions_test.rb +0 -36
  59. data/test/backend/fallbacks_test.rb +0 -219
  60. data/test/backend/interpolation_compiler_test.rb +0 -118
  61. data/test/backend/key_value_test.rb +0 -61
  62. data/test/backend/memoize_test.rb +0 -79
  63. data/test/backend/metadata_test.rb +0 -48
  64. data/test/backend/pluralization_test.rb +0 -45
  65. data/test/backend/simple_test.rb +0 -103
  66. data/test/backend/transliterator_test.rb +0 -84
  67. data/test/core_ext/hash_test.rb +0 -36
  68. data/test/gettext/api_test.rb +0 -214
  69. data/test/gettext/backend_test.rb +0 -92
  70. data/test/i18n/exceptions_test.rb +0 -117
  71. data/test/i18n/gettext_plural_keys_test.rb +0 -20
  72. data/test/i18n/interpolate_test.rb +0 -91
  73. data/test/i18n/load_path_test.rb +0 -34
  74. data/test/i18n/middleware_test.rb +0 -24
  75. data/test/i18n_test.rb +0 -462
  76. data/test/locale/fallbacks_test.rb +0 -133
  77. data/test/locale/tag/rfc4646_test.rb +0 -143
  78. data/test/locale/tag/simple_test.rb +0 -32
  79. data/test/run_all.rb +0 -20
  80. data/test/test_data/locales/de.po +0 -82
  81. data/test/test_data/locales/en.rb +0 -3
  82. data/test/test_data/locales/en.yml +0 -3
  83. data/test/test_data/locales/invalid/empty.yml +0 -0
  84. data/test/test_data/locales/invalid/syntax.yml +0 -4
  85. data/test/test_data/locales/plurals.rb +0 -113
  86. data/test/test_helper.rb +0 -61
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09643c444c40fe98042352f7f3e3e2df08e15ec5bee9f3d658b3e2fdeeb627a7'
4
- data.tar.gz: b924468907cca779632052d4ec166c79a9fc10f305a4a4f2c7be6ffeef6a5e47
3
+ metadata.gz: 2b03b84d726d3cafa7ea2ecaa6f258cb911696c338cb32c769f71b6d62ca5fb5
4
+ data.tar.gz: 7a489bf447e9fedee8fd043cc8ba324c0afc0c34701fbfeff5f15b1c1f4fa6b4
5
5
  SHA512:
6
- metadata.gz: 338556073a2c02752c1d3e5e3ba5ed70cb692d814ab05aca5a3d70ef3ee013aa3bb9d5cfc6aecbbd2a93e4732105a4c05c70968ea7f955c169cf69ad97462796
7
- data.tar.gz: a0a4eff7b73ac32730dae8b77f609c4cc6fca9b6c2f46d81f42d992e10dd8d1467dbc28985b38769f9cab2f6ac1d67e4309eb8a8df65d9fc38baa39b65343f18
6
+ metadata.gz: 534659c40a8df9eb3fd9a123b779de0f1b66471ecd3fb35e2083128193f51aeea95df6aad06039b6309932a6efb3426c525a8759e70c1104754fdd921732d6cd
7
+ data.tar.gz: caddebe447a797be3dde1607e74d826588ee13ee1317c6abfbb08cbd8a22d79a6aa10a84d3fd21fa45ca9348317aa0e0aafe1c408d4e1c989fbee7f09cd87606
data/README.md CHANGED
@@ -1,12 +1,55 @@
1
1
  # Ruby I18n
2
2
 
3
- [![Build Status](https://api.travis-ci.org/svenfuchs/i18n.svg?branch=master)](https://travis-ci.org/svenfuchs/i18n)
3
+ [![Build Status](https://api.travis-ci.org/ruby-i18n/i18n.svg?branch=master)](https://travis-ci.org/ruby-i18n/i18n)
4
4
 
5
5
  Ruby Internationalization and localization solution.
6
6
 
7
- [See the Rails Guide](http://guides.rubyonrails.org/i18n.html) for an example of its usage. (Note: This library can be used independently from Rails.)
7
+ Currently maintained by @radar.
8
8
 
9
- Features:
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 features:
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/svenfuchs/i18n/wiki/Gettext)
70
+ * [Gettext support](https://github.com/ruby-i18n/i18n/wiki/Gettext)
28
71
  * Translation metadata
29
72
 
30
- Alternative backends:
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/svenfuchs/i18n/wiki/Resources).
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)
@@ -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) or YAML files (*.yml). See #load_rb and #load_yml
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 '%A' then I18n.t(:"date.day_names", :locale => locale, :format => format)[object.wday]
241
- when '%b' then I18n.t(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon]
242
- when '%B' then I18n.t(:"date.month_names", :locale => locale, :format => format)[object.mon]
243
- when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).upcase if object.respond_to? :hour
244
- when '%P' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).downcase if object.respond_to? :hour
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)
@@ -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)}/#{USE_INSPECT_HASH ? digest_item(options.inspect) : digest_item(options)}"
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
@@ -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)
@@ -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 store_translations(locale, data, options = {})
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
- begin
43
- options[:fallback_in_progress] = true
44
- I18n.fallbacks[locale].each do |fallback|
45
- begin
46
- catch(:exception) do
47
- result = super(fallback, key, options)
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?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module I18n
2
4
  module Backend
3
5
  # This module contains several helpers to assist flattening translations.
@@ -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 store_translations(locale, data, options = {})
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
@@ -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, value| RESERVED_KEYS.include?(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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # I18n Pluralization are useful when you want your application to
2
4
  # customize pluralization rules.
3
5
  #