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.
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
  #