i18n 1.0.1 → 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 (73) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +54 -13
  3. data/lib/i18n.rb +53 -27
  4. data/lib/i18n/backend.rb +1 -0
  5. data/lib/i18n/backend/base.rb +36 -9
  6. data/lib/i18n/backend/cache.rb +2 -5
  7. data/lib/i18n/backend/cache_file.rb +36 -0
  8. data/lib/i18n/backend/chain.rb +28 -0
  9. data/lib/i18n/backend/gettext.rb +2 -0
  10. data/lib/i18n/backend/key_value.rb +27 -0
  11. data/lib/i18n/backend/memoize.rb +6 -0
  12. data/lib/i18n/backend/simple.rb +22 -6
  13. data/lib/i18n/config.rb +17 -1
  14. data/lib/i18n/core_ext/hash.rb +42 -24
  15. data/lib/i18n/exceptions.rb +20 -15
  16. data/lib/i18n/interpolate/ruby.rb +5 -3
  17. data/lib/i18n/locale/fallbacks.rb +1 -1
  18. data/lib/i18n/tests/interpolation.rb +1 -1
  19. data/lib/i18n/tests/localization/date.rb +28 -6
  20. data/lib/i18n/tests/localization/date_time.rb +27 -6
  21. data/lib/i18n/tests/localization/time.rb +26 -4
  22. data/lib/i18n/version.rb +1 -1
  23. metadata +24 -58
  24. data/gemfiles/Gemfile.rails-3.2.x +0 -10
  25. data/gemfiles/Gemfile.rails-4.0.x +0 -10
  26. data/gemfiles/Gemfile.rails-4.1.x +0 -10
  27. data/gemfiles/Gemfile.rails-4.2.x +0 -10
  28. data/gemfiles/Gemfile.rails-5.0.x +0 -10
  29. data/gemfiles/Gemfile.rails-5.1.x +0 -10
  30. data/gemfiles/Gemfile.rails-master +0 -10
  31. data/lib/i18n/core_ext/kernel/suppress_warnings.rb +0 -8
  32. data/lib/i18n/core_ext/string/interpolate.rb +0 -9
  33. data/test/api/all_features_test.rb +0 -58
  34. data/test/api/cascade_test.rb +0 -28
  35. data/test/api/chain_test.rb +0 -24
  36. data/test/api/fallbacks_test.rb +0 -30
  37. data/test/api/key_value_test.rb +0 -24
  38. data/test/api/memoize_test.rb +0 -56
  39. data/test/api/override_test.rb +0 -42
  40. data/test/api/pluralization_test.rb +0 -30
  41. data/test/api/simple_test.rb +0 -28
  42. data/test/backend/cache_test.rb +0 -109
  43. data/test/backend/cascade_test.rb +0 -86
  44. data/test/backend/chain_test.rb +0 -122
  45. data/test/backend/exceptions_test.rb +0 -36
  46. data/test/backend/fallbacks_test.rb +0 -219
  47. data/test/backend/interpolation_compiler_test.rb +0 -118
  48. data/test/backend/key_value_test.rb +0 -61
  49. data/test/backend/memoize_test.rb +0 -79
  50. data/test/backend/metadata_test.rb +0 -48
  51. data/test/backend/pluralization_test.rb +0 -45
  52. data/test/backend/simple_test.rb +0 -103
  53. data/test/backend/transliterator_test.rb +0 -84
  54. data/test/core_ext/hash_test.rb +0 -36
  55. data/test/gettext/api_test.rb +0 -214
  56. data/test/gettext/backend_test.rb +0 -92
  57. data/test/i18n/exceptions_test.rb +0 -117
  58. data/test/i18n/gettext_plural_keys_test.rb +0 -20
  59. data/test/i18n/interpolate_test.rb +0 -91
  60. data/test/i18n/load_path_test.rb +0 -34
  61. data/test/i18n/middleware_test.rb +0 -24
  62. data/test/i18n_test.rb +0 -462
  63. data/test/locale/fallbacks_test.rb +0 -133
  64. data/test/locale/tag/rfc4646_test.rb +0 -143
  65. data/test/locale/tag/simple_test.rb +0 -32
  66. data/test/run_all.rb +0 -20
  67. data/test/test_data/locales/de.po +0 -82
  68. data/test/test_data/locales/en.rb +0 -3
  69. data/test/test_data/locales/en.yml +0 -3
  70. data/test/test_data/locales/invalid/empty.yml +0 -0
  71. data/test/test_data/locales/invalid/syntax.yml +0 -4
  72. data/test/test_data/locales/plurals.rb +0 -113
  73. data/test/test_helper.rb +0 -61
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6abb11703c717406a1a4e5df13e88679656a3bb798cd0141e4ef9961fed64056
4
- data.tar.gz: 1e633332f63054b3919c887a853a1bb4348025550531bf3f499a924a1b37acf5
3
+ metadata.gz: 2b03b84d726d3cafa7ea2ecaa6f258cb911696c338cb32c769f71b6d62ca5fb5
4
+ data.tar.gz: 7a489bf447e9fedee8fd043cc8ba324c0afc0c34701fbfeff5f15b1c1f4fa6b4
5
5
  SHA512:
6
- metadata.gz: 6fe991afffcec28225b53932a1977c6c30ee6f8a79a0256380a552f25ca6d0bffd3b21452d8177529d97aac9f880e1296072804942c0b99a379ecfeb6c8ca446
7
- data.tar.gz: b8a7625539fe9e5c0c49b0a85a3bce54c1fb46e68ed4910870aee8c6f37afb589888b3ba87a6786e4364021a831619874f796a38eea6d0e6b285f67e232b3926
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)
@@ -69,6 +69,13 @@ module I18n
69
69
  config.backend.reload!
70
70
  end
71
71
 
72
+ # Tells the backend to load translations now. Used in situations like the
73
+ # Rails production environment. Backends can implement whatever strategy
74
+ # is useful.
75
+ def eager_load!
76
+ config.backend.eager_load!
77
+ end
78
+
72
79
  # Translates, pluralizes and interpolates a given key using a given locale,
73
80
  # scope, and default, as well as interpolation values.
74
81
  #
@@ -169,15 +176,13 @@ module I18n
169
176
  # from the argument values passed to #translate. Therefor your lambdas should
170
177
  # always return the same translations/values per unique combination of argument
171
178
  # values.
172
- def translate(*args)
173
- options = args.last.is_a?(Hash) ? args.pop.dup : {}
174
- key = args.shift
175
- backend = config.backend
176
- locale = options.delete(:locale) || config.locale
177
- handling = options.delete(:throw) && :throw || options.delete(:raise) && :raise # TODO deprecate :raise
178
-
179
+ def translate(key = nil, *, throw: false, raise: false, locale: nil, **options) # TODO deprecate :raise
180
+ locale ||= config.locale
181
+ raise Disabled.new('t') if locale == false
179
182
  enforce_available_locales!(locale)
180
183
 
184
+ backend = config.backend
185
+
181
186
  result = catch(:exception) do
182
187
  if key.is_a?(Array)
183
188
  key.map { |k| backend.translate(locale, k, options) }
@@ -185,7 +190,12 @@ module I18n
185
190
  backend.translate(locale, key, options)
186
191
  end
187
192
  end
188
- result.is_a?(MissingTranslation) ? handle_exception(handling, result, locale, key, options) : result
193
+
194
+ if result.is_a?(MissingTranslation)
195
+ handle_exception((throw && :throw || raise && :raise), result, locale, key, options)
196
+ else
197
+ result
198
+ end
189
199
  end
190
200
  alias :t :translate
191
201
 
@@ -197,7 +207,9 @@ module I18n
197
207
  alias :t! :translate!
198
208
 
199
209
  # Returns true if a translation exists for a given key, otherwise returns false.
200
- def exists?(key, locale = config.locale)
210
+ def exists?(key, _locale = nil, locale: _locale)
211
+ locale ||= config.locale
212
+ raise Disabled.new('exists?') if locale == false
201
213
  raise I18n::ArgumentError if key.is_a?(String) && key.empty?
202
214
  config.backend.exists?(locale, key)
203
215
  end
@@ -253,37 +265,40 @@ module I18n
253
265
  # I18n.transliterate("Jürgen") # => "Juergen"
254
266
  # I18n.transliterate("Jürgen", :locale => :en) # => "Jurgen"
255
267
  # I18n.transliterate("Jürgen", :locale => :de) # => "Juergen"
256
- def transliterate(*args)
257
- options = args.pop.dup if args.last.is_a?(Hash)
258
- key = args.shift
259
- locale = options && options.delete(:locale) || config.locale
260
- handling = options && (options.delete(:throw) && :throw || options.delete(:raise) && :raise)
261
- replacement = options && options.delete(:replacement)
268
+ def transliterate(key, *, throw: false, raise: false, locale: nil, replacement: nil, **options)
269
+ locale ||= config.locale
270
+ raise Disabled.new('transliterate') if locale == false
262
271
  enforce_available_locales!(locale)
272
+
263
273
  config.backend.transliterate(locale, key, replacement)
264
274
  rescue I18n::ArgumentError => exception
265
- handle_exception(handling, exception, locale, key, options || {})
275
+ handle_exception((throw && :throw || raise && :raise), exception, locale, key, options)
266
276
  end
267
277
 
268
278
  # Localizes certain objects, such as dates and numbers to local formatting.
269
- def localize(object, options = nil)
270
- options = options ? options.dup : {}
271
- locale = options.delete(:locale) || config.locale
272
- format = options.delete(:format) || :default
279
+ def localize(object, locale: nil, format: nil, **options)
280
+ locale ||= config.locale
281
+ raise Disabled.new('l') if locale == false
273
282
  enforce_available_locales!(locale)
283
+
284
+ format ||= :default
274
285
  config.backend.localize(locale, object, format, options)
275
286
  end
276
287
  alias :l :localize
277
288
 
278
289
  # Executes block with given I18n.locale set.
279
290
  def with_locale(tmp_locale = nil)
280
- if tmp_locale
291
+ if tmp_locale == nil
292
+ yield
293
+ else
281
294
  current_locale = self.locale
282
- self.locale = tmp_locale
295
+ self.locale = tmp_locale
296
+ begin
297
+ yield
298
+ ensure
299
+ self.locale = current_locale
300
+ end
283
301
  end
284
- yield
285
- ensure
286
- self.locale = current_locale if tmp_locale
287
302
  end
288
303
 
289
304
  # Merges the given locale, key and scope into a single array of keys.
@@ -307,7 +322,7 @@ module I18n
307
322
 
308
323
  # Raises an InvalidLocale exception when the passed locale is not available.
309
324
  def enforce_available_locales!(locale)
310
- if config.enforce_available_locales
325
+ if locale != false && config.enforce_available_locales
311
326
  raise I18n::InvalidLocale.new(locale) if !locale_available?(locale)
312
327
  end
313
328
  end
@@ -362,7 +377,18 @@ module I18n
362
377
  else
363
378
  keys = key.to_s.split(separator)
364
379
  keys.delete('')
365
- keys.map! { |k| k.to_sym }
380
+ keys.map! do |k|
381
+ case k
382
+ when /\A[-+]?[1-9]\d*\z/ # integer
383
+ k.to_i
384
+ when 'true'
385
+ true
386
+ when 'false'
387
+ false
388
+ else
389
+ k.to_sym
390
+ end
391
+ end
366
392
  keys
367
393
  end
368
394
  end
@@ -5,6 +5,7 @@ module I18n
5
5
  autoload :Base, 'i18n/backend/base'
6
6
  autoload :InterpolationCompiler, 'i18n/backend/interpolation_compiler'
7
7
  autoload :Cache, 'i18n/backend/cache'
8
+ autoload :CacheFile, 'i18n/backend/cache_file'
8
9
  autoload :Cascade, 'i18n/backend/cascade'
9
10
  autoload :Chain, 'i18n/backend/chain'
10
11
  autoload :Fallbacks, 'i18n/backend/fallbacks'
@@ -1,16 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'yaml'
4
+ require 'json'
4
5
  require 'i18n/core_ext/hash'
5
- require 'i18n/core_ext/kernel/suppress_warnings'
6
6
 
7
7
  module I18n
8
8
  module Backend
9
9
  module Base
10
+ using I18n::HashRefinements
10
11
  include I18n::Backend::Transliterator
11
12
 
12
13
  # Accepts a list of paths to translation files. Loads translations from
13
- # 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
14
15
  # for details.
15
16
  def load_translations(*filenames)
16
17
  filenames = I18n.load_path if filenames.empty?
@@ -94,10 +95,19 @@ module I18n
94
95
  end
95
96
 
96
97
  def reload!
98
+ eager_load! if eager_loaded?
99
+ end
100
+
101
+ def eager_load!
102
+ @eager_loaded = true
97
103
  end
98
104
 
99
105
  protected
100
106
 
107
+ def eager_loaded?
108
+ @eager_loaded ||= false
109
+ end
110
+
101
111
  # The method which actually looks up for the translation in the store.
102
112
  def lookup(locale, key, scope = [], options = EMPTY_HASH)
103
113
  raise NotImplementedError
@@ -234,18 +244,35 @@ module I18n
234
244
  raise InvalidLocaleData.new(filename, e.inspect)
235
245
  end
236
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
237
258
 
238
259
  def translate_localization_format(locale, object, format, options)
239
- format.to_s.gsub(/%[aAbBpP]/) do |match|
260
+ format.to_s.gsub(/%(|\^)[aAbBpP]/) do |match|
240
261
  case match
241
- when '%a' then I18n.t(:"date.abbr_day_names", :locale => locale, :format => format)[object.wday]
242
- when '%A' then I18n.t(:"date.day_names", :locale => locale, :format => format)[object.wday]
243
- when '%b' then I18n.t(:"date.abbr_month_names", :locale => locale, :format => format)[object.mon]
244
- when '%B' then I18n.t(:"date.month_names", :locale => locale, :format => format)[object.mon]
245
- when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am : :pm}", :locale => locale, :format => format).upcase if object.respond_to? :hour
246
- 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
247
272
  end
248
273
  end
274
+ rescue MissingTranslationData => e
275
+ e.message
249
276
  end
250
277
 
251
278
  def pluralization_key(entry, count)
@@ -100,16 +100,13 @@ module I18n
100
100
 
101
101
  def cache_key(locale, key, options)
102
102
  # This assumes that only simple, native Ruby values are passed to I18n.translate.
103
- "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)}"
104
104
  end
105
105
 
106
106
  private
107
- # In Ruby < 1.9 the following is true: { :foo => 1, :bar => 2 }.hash == { :foo => 2, :bar => 1 }.hash
108
- # Therefore we must use the hash of the inspect string instead to avoid cache key colisions.
109
- USE_INSPECT_HASH = RUBY_VERSION <= "1.9"
110
107
 
111
108
  def digest_item(key)
112
- 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
113
110
  end
114
111
  end
115
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
@@ -17,6 +17,8 @@ module I18n
17
17
  # The implementation assumes that all backends added to the Chain implement
18
18
  # a lookup method with the same API as Simple backend does.
19
19
  class Chain
20
+ using I18n::HashRefinements
21
+
20
22
  module Implementation
21
23
  include Base
22
24
 
@@ -26,10 +28,23 @@ module I18n
26
28
  self.backends = backends
27
29
  end
28
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
+
29
40
  def reload!
30
41
  backends.each { |backend| backend.reload! }
31
42
  end
32
43
 
44
+ def eager_load!
45
+ backends.each { |backend| backend.eager_load! }
46
+ end
47
+
33
48
  def store_translations(locale, data, options = EMPTY_HASH)
34
49
  backends.first.store_translations(locale, data, options)
35
50
  end
@@ -74,6 +89,19 @@ module I18n
74
89
  end
75
90
 
76
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
+
77
105
  def namespace_lookup?(result, options)
78
106
  result.is_a?(Hash) && !options.has_key?(:count)
79
107
  end