i18n 0.4.0 → 1.14.4

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 (58) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +0 -0
  3. data/README.md +127 -0
  4. data/lib/i18n/backend/base.rb +189 -111
  5. data/lib/i18n/backend/cache.rb +58 -22
  6. data/lib/i18n/backend/cache_file.rb +36 -0
  7. data/lib/i18n/backend/cascade.rb +9 -10
  8. data/lib/i18n/backend/chain.rb +95 -42
  9. data/lib/i18n/backend/fallbacks.rb +68 -22
  10. data/lib/i18n/backend/flatten.rb +13 -8
  11. data/lib/i18n/backend/gettext.rb +33 -25
  12. data/lib/i18n/backend/interpolation_compiler.rb +12 -14
  13. data/lib/i18n/backend/key_value.rb +112 -10
  14. data/lib/i18n/backend/lazy_loadable.rb +184 -0
  15. data/lib/i18n/backend/memoize.rb +13 -7
  16. data/lib/i18n/backend/metadata.rb +12 -6
  17. data/lib/i18n/backend/pluralization.rb +64 -25
  18. data/lib/i18n/backend/simple.rb +44 -18
  19. data/lib/i18n/backend/transliterator.rb +43 -33
  20. data/lib/i18n/backend.rb +5 -3
  21. data/lib/i18n/config.rb +91 -10
  22. data/lib/i18n/exceptions.rb +118 -22
  23. data/lib/i18n/gettext/helpers.rb +14 -4
  24. data/lib/i18n/gettext/po_parser.rb +7 -7
  25. data/lib/i18n/gettext.rb +6 -5
  26. data/lib/i18n/interpolate/ruby.rb +53 -0
  27. data/lib/i18n/locale/fallbacks.rb +33 -26
  28. data/lib/i18n/locale/tag/parents.rb +8 -8
  29. data/lib/i18n/locale/tag/rfc4646.rb +0 -2
  30. data/lib/i18n/locale/tag/simple.rb +2 -4
  31. data/lib/i18n/locale.rb +2 -0
  32. data/lib/i18n/middleware.rb +17 -0
  33. data/lib/i18n/tests/basics.rb +58 -0
  34. data/lib/i18n/tests/defaults.rb +52 -0
  35. data/lib/i18n/tests/interpolation.rb +167 -0
  36. data/lib/i18n/tests/link.rb +66 -0
  37. data/lib/i18n/tests/localization/date.rb +122 -0
  38. data/lib/i18n/tests/localization/date_time.rb +103 -0
  39. data/lib/i18n/tests/localization/procs.rb +118 -0
  40. data/lib/i18n/tests/localization/time.rb +103 -0
  41. data/lib/i18n/tests/localization.rb +19 -0
  42. data/lib/i18n/tests/lookup.rb +81 -0
  43. data/lib/i18n/tests/pluralization.rb +35 -0
  44. data/lib/i18n/tests/procs.rb +66 -0
  45. data/lib/i18n/tests.rb +14 -0
  46. data/lib/i18n/utils.rb +55 -0
  47. data/lib/i18n/version.rb +3 -1
  48. data/lib/i18n.rb +200 -87
  49. metadata +64 -56
  50. data/CHANGELOG.textile +0 -135
  51. data/README.textile +0 -93
  52. data/lib/i18n/backend/active_record/missing.rb +0 -65
  53. data/lib/i18n/backend/active_record/store_procs.rb +0 -38
  54. data/lib/i18n/backend/active_record/translation.rb +0 -93
  55. data/lib/i18n/backend/active_record.rb +0 -61
  56. data/lib/i18n/backend/cldr.rb +0 -100
  57. data/lib/i18n/core_ext/hash.rb +0 -29
  58. data/lib/i18n/core_ext/string/interpolate.rb +0 -98
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
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 = OpenSSL::Digest::SHA256.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,7 +1,5 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- # EXPERIMENTAL
4
- #
5
3
  # The Cascade module adds the ability to do cascading lookups to backends that
6
4
  # are compatible to the Simple backend.
7
5
  #
@@ -35,22 +33,23 @@
35
33
  module I18n
36
34
  module Backend
37
35
  module Cascade
38
- def lookup(locale, key, scope = [], options = {})
36
+ def lookup(locale, key, scope = [], options = EMPTY_HASH)
39
37
  return super unless cascade = options[:cascade]
40
38
 
39
+ cascade = { :step => 1 } unless cascade.is_a?(Hash)
40
+ step = cascade[:step] || 1
41
+ offset = cascade[:offset] || 1
41
42
  separator = options[:separator] || I18n.default_separator
42
43
  skip_root = cascade.has_key?(:skip_root) ? cascade[:skip_root] : true
43
- step = cascade[:step]
44
44
 
45
- keys = I18n.normalize_keys(nil, key, nil, separator)
46
- offset = options[:cascade][:offset] || keys.length
47
- scope = I18n.normalize_keys(nil, nil, scope, separator) + keys
48
- key = scope.slice!(-offset, offset).join(separator)
45
+ scope = I18n.normalize_keys(nil, key, scope, separator)
46
+ key = (scope.slice!(-offset, offset) || []).join(separator)
49
47
 
50
48
  begin
51
49
  result = super
52
50
  return result unless result.nil?
53
- end while !scope.empty? && scope.slice!(-step, step) && (!scope.empty? || !skip_root)
51
+ scope = scope.dup
52
+ end while (!scope.empty? || !skip_root) && scope.slice!(-step, step)
54
53
  end
55
54
  end
56
55
  end
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module I18n
4
4
  module Backend
@@ -16,62 +16,115 @@ module I18n
16
16
  #
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
+ #
20
+ # Fallback translations using the :default option are only used by the last backend of a chain.
19
21
  class Chain
20
- include Base
22
+ module Implementation
23
+ include Base
21
24
 
22
- attr_accessor :backends
25
+ attr_accessor :backends
23
26
 
24
- def initialize(*backends)
25
- self.backends = backends
26
- end
27
+ def initialize(*backends)
28
+ self.backends = backends
29
+ end
27
30
 
28
- def reload!
29
- backends.each { |backend| backend.reload! }
30
- end
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
31
39
 
32
- def store_translations(locale, data, options = {})
33
- backends.first.store_translations(locale, data, options = {})
34
- end
40
+ def reload!
41
+ backends.each { |backend| backend.reload! }
42
+ end
35
43
 
36
- def available_locales
37
- backends.map { |backend| backend.available_locales }.flatten.uniq
38
- end
44
+ def eager_load!
45
+ backends.each { |backend| backend.eager_load! }
46
+ end
47
+
48
+ def store_translations(locale, data, options = EMPTY_HASH)
49
+ backends.first.store_translations(locale, data, options)
50
+ end
51
+
52
+ def available_locales
53
+ backends.map { |backend| backend.available_locales }.flatten.uniq
54
+ end
55
+
56
+ def translate(locale, key, default_options = EMPTY_HASH)
57
+ namespace = nil
58
+ options = Utils.except(default_options, :default)
39
59
 
40
- def translate(locale, key, options = {})
41
- return key.map { |k| translate(locale, k, options) } if key.is_a?(Array)
42
-
43
- default = options.delete(:default)
44
- namespace = {}
45
- backends.each do |backend|
46
- begin
47
- options.update(:default => default) if default and backend == backends.last
48
- translation = backend.translate(locale, key, options)
49
- if namespace_lookup?(translation, options)
50
- namespace.update(translation)
51
- elsif !translation.nil?
52
- return translation
60
+ backends.each do |backend|
61
+ catch(:exception) do
62
+ options = default_options if backend == backends.last
63
+ translation = backend.translate(locale, key, options)
64
+ if namespace_lookup?(translation, options)
65
+ namespace = _deep_merge(translation, namespace || {})
66
+ elsif !translation.nil? || (options.key?(:default) && options[:default].nil?)
67
+ return translation
68
+ end
53
69
  end
54
- rescue MissingTranslationData
55
70
  end
71
+
72
+ return namespace if namespace
73
+ throw(:exception, I18n::MissingTranslation.new(locale, key, options))
56
74
  end
57
- return namespace unless namespace.empty?
58
- raise(I18n::MissingTranslationData.new(locale, key, options))
59
- end
60
75
 
61
- def localize(locale, object, format = :default, options = {})
62
- backends.each do |backend|
63
- begin
64
- result = backend.localize(locale, object, format, options) and return result
65
- rescue MissingTranslationData
76
+ def exists?(locale, key, options = EMPTY_HASH)
77
+ backends.any? do |backend|
78
+ backend.exists?(locale, key, options)
66
79
  end
67
80
  end
68
- raise(I18n::MissingTranslationData.new(locale, format, options))
69
- end
70
81
 
71
- protected
72
- def namespace_lookup?(result, options)
73
- result.is_a?(Hash) and not options.has_key?(:count)
82
+ def localize(locale, object, format = :default, options = EMPTY_HASH)
83
+ backends.each do |backend|
84
+ catch(:exception) do
85
+ result = backend.localize(locale, object, format, options) and return result
86
+ end
87
+ end
88
+ throw(:exception, I18n::MissingTranslation.new(locale, format, options))
74
89
  end
90
+
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.reverse.each_with_object({}) do |backend, memo|
100
+ partial_translations = backend.instance_eval do
101
+ init_translations unless initialized?
102
+ translations
103
+ end
104
+ Utils.deep_merge!(memo, partial_translations) { |_, a, b| b || a }
105
+ end
106
+ end
107
+
108
+ def namespace_lookup?(result, options)
109
+ result.is_a?(Hash) && !options.has_key?(:count)
110
+ end
111
+
112
+ private
113
+ # This is approximately what gets used in ActiveSupport.
114
+ # However since we are not guaranteed to run in an ActiveSupport context
115
+ # it is wise to have our own copy. We underscore it
116
+ # to not pollute the namespace of the including class.
117
+ def _deep_merge(hash, other_hash)
118
+ copy = hash.dup
119
+ other_hash.each_pair do |k,v|
120
+ value_from_other = hash[k]
121
+ copy[k] = value_from_other.is_a?(Hash) && v.is_a?(Hash) ? _deep_merge(value_from_other, v) : v
122
+ end
123
+ copy
124
+ end
125
+ end
126
+
127
+ include Implementation
75
128
  end
76
129
  end
77
130
  end
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  # I18n locale fallbacks are useful when you want your application to use
4
4
  # translations from other locales when translations for the current locale are
@@ -8,7 +8,7 @@
8
8
  # To enable locale fallbacks you can simply include the Fallbacks module to
9
9
  # the Simple backend - or whatever other backend you are using:
10
10
  #
11
- # I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
11
+ # I18n::Backend::Simple.include(I18n::Backend::Fallbacks)
12
12
  module I18n
13
13
  @@fallbacks = nil
14
14
 
@@ -16,11 +16,13 @@ module I18n
16
16
  # Returns the current fallbacks implementation. Defaults to +I18n::Locale::Fallbacks+.
17
17
  def fallbacks
18
18
  @@fallbacks ||= I18n::Locale::Fallbacks.new
19
+ Thread.current[:i18n_fallbacks] || @@fallbacks
19
20
  end
20
21
 
21
22
  # Sets the current fallbacks implementation. Use this to set a different fallbacks implementation.
22
23
  def fallbacks=(fallbacks)
23
- @@fallbacks = fallbacks
24
+ @@fallbacks = fallbacks.is_a?(Array) ? I18n::Locale::Fallbacks.new(fallbacks) : fallbacks
25
+ Thread.current[:i18n_fallbacks] = @@fallbacks
24
26
  end
25
27
  end
26
28
 
@@ -31,39 +33,83 @@ module I18n
31
33
  # locale :"de-DE" it might try the locales :"de-DE", :de and :en
32
34
  # (depends on the fallbacks implementation) until it finds a result with
33
35
  # the given options. If it does not find any result for any of the
34
- # locales it will then raise a MissingTranslationData exception as
35
- # usual.
36
+ # locales it will then throw MissingTranslation as usual.
36
37
  #
37
- # The default option takes precedence over fallback locales
38
- # only when it's not a String. When default contains String it
39
- # is evaluated after fallback locales.
40
- def translate(locale, key, options = {})
41
- default = extract_string_default!(options) if options[:default]
38
+ # The default option takes precedence over fallback locales only when
39
+ # it's a Symbol. When the default contains a String, Proc or Hash
40
+ # it is evaluated last after all the fallback locales have been tried.
41
+ def translate(locale, key, options = EMPTY_HASH)
42
+ return super unless options.fetch(:fallback, true)
43
+ return super if options[:fallback_in_progress]
44
+ default = extract_non_symbol_default!(options) if options[:default]
42
45
 
46
+ fallback_options = options.merge(:fallback_in_progress => true, fallback_original_locale: locale)
43
47
  I18n.fallbacks[locale].each do |fallback|
44
48
  begin
45
- result = super(fallback, key, options)
46
- return result unless result.nil?
47
- rescue I18n::MissingTranslationData
49
+ catch(:exception) do
50
+ result = super(fallback, key, fallback_options)
51
+ unless result.nil?
52
+ on_fallback(locale, fallback, key, options) if locale.to_s != fallback.to_s
53
+ return result
54
+ end
55
+ end
56
+ rescue I18n::InvalidLocale
57
+ # we do nothing when the locale is invalid, as this is a fallback anyways.
48
58
  end
49
59
  end
50
60
 
61
+ return if options.key?(:default) && options[:default].nil?
62
+
51
63
  return super(locale, nil, options.merge(:default => default)) if default
52
- raise(I18n::MissingTranslationData.new(locale, key, options))
64
+ throw(:exception, I18n::MissingTranslation.new(locale, key, options))
53
65
  end
54
66
 
55
- def extract_string_default!(options)
56
- defaults = Array(options[:default])
57
- if index = find_first_string_default(defaults)
58
- options[:default] = defaults[0, index]
59
- defaults[index]
67
+ def resolve_entry(locale, object, subject, options = EMPTY_HASH)
68
+ return subject if options[:resolve] == false
69
+ result = catch(:exception) do
70
+ options.delete(:fallback_in_progress) if options.key?(:fallback_in_progress)
71
+
72
+ case subject
73
+ when Symbol
74
+ I18n.translate(subject, **options.merge(:locale => options[:fallback_original_locale], :throw => true))
75
+ when Proc
76
+ date_or_time = options.delete(:object) || object
77
+ resolve_entry(options[:fallback_original_locale], object, subject.call(date_or_time, **options))
78
+ else
79
+ subject
80
+ end
60
81
  end
82
+ result unless result.is_a?(MissingTranslation)
61
83
  end
62
84
 
63
- def find_first_string_default(defaults)
64
- defaults.each_index { |ix| return ix if String === defaults[ix] }
65
- nil
85
+ def extract_non_symbol_default!(options)
86
+ defaults = [options[:default]].flatten
87
+ first_non_symbol_default = defaults.detect{|default| !default.is_a?(Symbol)}
88
+ if first_non_symbol_default
89
+ options[:default] = defaults[0, defaults.index(first_non_symbol_default)]
90
+ end
91
+ return first_non_symbol_default
92
+ end
93
+
94
+ def exists?(locale, key, options = EMPTY_HASH)
95
+ return super unless options.fetch(:fallback, true)
96
+ I18n.fallbacks[locale].each do |fallback|
97
+ begin
98
+ return true if super(fallback, key, options)
99
+ rescue I18n::InvalidLocale
100
+ # we do nothing when the locale is invalid, as this is a fallback anyways.
101
+ end
102
+ end
103
+
104
+ false
66
105
  end
106
+
107
+ private
108
+
109
+ # Overwrite on_fallback to add specified logic when the fallback succeeds.
110
+ def on_fallback(_original_locale, _fallback_locale, _key, _options)
111
+ nil
112
+ end
67
113
  end
68
114
  end
69
115
  end
@@ -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.
@@ -16,14 +18,17 @@ module I18n
16
18
  # and creates way less objects than the one at I18n.normalize_keys.
17
19
  # It also handles escaping the translation keys.
18
20
  def self.normalize_flat_keys(locale, key, scope, separator)
19
- keys = [scope, key].flatten.compact
21
+ keys = [scope, key]
22
+ keys.flatten!
23
+ keys.compact!
24
+
20
25
  separator ||= I18n.default_separator
21
26
 
22
27
  if separator != FLATTEN_SEPARATOR
23
- keys.map! do |k|
24
- k.to_s.tr("#{FLATTEN_SEPARATOR}#{separator}",
25
- "#{SEPARATOR_ESCAPE_CHAR}#{FLATTEN_SEPARATOR}")
26
- end
28
+ from_str = "#{FLATTEN_SEPARATOR}#{separator}"
29
+ to_str = "#{SEPARATOR_ESCAPE_CHAR}#{FLATTEN_SEPARATOR}"
30
+
31
+ keys.map! { |k| k.to_s.tr from_str, to_str }
27
32
  end
28
33
 
29
34
  keys.join(".")
@@ -43,7 +48,7 @@ module I18n
43
48
 
44
49
  # Store flattened links.
45
50
  def links
46
- @links ||= Hash.new { |h,k| h[k] = {} }
51
+ @links ||= I18n.new_double_nested_cache
47
52
  end
48
53
 
49
54
  # Flatten keys for nested Hashes by chaining up keys:
@@ -99,7 +104,7 @@ module I18n
99
104
  end
100
105
 
101
106
  def find_link(locale, key) #:nodoc:
102
- links[locale].each do |from, to|
107
+ links[locale].each_pair do |from, to|
103
108
  return [from, to] if key[0, from.length] == from
104
109
  end && nil
105
110
  end
@@ -110,4 +115,4 @@ module I18n
110
115
 
111
116
  end
112
117
  end
113
- end
118
+ end
@@ -1,26 +1,35 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'i18n/gettext'
4
4
  require 'i18n/gettext/po_parser'
5
5
 
6
- # Experimental support for using Gettext po files to store translations.
7
- #
8
- # To use this you can simply include the module to the Simple backend - or
9
- # whatever other backend you are using.
10
- #
11
- # I18n::Backend::Simple.send(:include, I18n::Backend::Gettext)
12
- #
13
- # Now you should be able to include your Gettext translation (*.po) files to
14
- # the I18n.load_path so they're loaded to the backend and you can use them as
15
- # usual:
16
- #
17
- # I18n.load_path += Dir["path/to/locales/*.po"]
18
- #
19
- # Following the Gettext convention this implementation expects that your
20
- # translation files are named by their locales. E.g. the file en.po would
21
- # contain the translations for the English locale.
22
6
  module I18n
23
7
  module Backend
8
+ # Experimental support for using Gettext po files to store translations.
9
+ #
10
+ # To use this you can simply include the module to the Simple backend - or
11
+ # whatever other backend you are using.
12
+ #
13
+ # I18n::Backend::Simple.include(I18n::Backend::Gettext)
14
+ #
15
+ # Now you should be able to include your Gettext translation (*.po) files to
16
+ # the +I18n.load_path+ so they're loaded to the backend and you can use them as
17
+ # usual:
18
+ #
19
+ # I18n.load_path += Dir["path/to/locales/*.po"]
20
+ #
21
+ # Following the Gettext convention this implementation expects that your
22
+ # translation files are named by their locales. E.g. the file en.po would
23
+ # contain the translations for the English locale.
24
+ #
25
+ # To translate text <b>you must use</b> one of the translate methods provided by
26
+ # I18n::Gettext::Helpers.
27
+ #
28
+ # include I18n::Gettext::Helpers
29
+ # puts _("some string")
30
+ #
31
+ # Without it strings containing periods (".") will not be translated.
32
+
24
33
  module Gettext
25
34
  class PoData < Hash
26
35
  def set_comment(msgid_or_sym, comment)
@@ -32,7 +41,7 @@ module I18n
32
41
  def load_po(filename)
33
42
  locale = ::File.basename(filename, '.po').to_sym
34
43
  data = normalize(locale, parse(filename))
35
- { locale => data }
44
+ [{ locale => data }, false]
36
45
  end
37
46
 
38
47
  def parse(filename)
@@ -42,16 +51,15 @@ module I18n
42
51
  def normalize(locale, data)
43
52
  data.inject({}) do |result, (key, value)|
44
53
  unless key.nil? || key.empty?
54
+ key = key.gsub(I18n::Gettext::CONTEXT_SEPARATOR, '|')
45
55
  key, value = normalize_pluralization(locale, key, value) if key.index("\000")
46
56
 
47
57
  parts = key.split('|').reverse
48
- normalized = parts.inject({}) do |normalized, part|
49
- normalized = { part => normalized.empty? ? value : normalized }
58
+ normalized = parts.inject({}) do |_normalized, part|
59
+ { part => _normalized.empty? ? value : _normalized }
50
60
  end
51
61
 
52
- # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
53
- merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
54
- result.merge!(normalized, &merger)
62
+ Utils.deep_merge!(result, normalized)
55
63
  end
56
64
  result
57
65
  end
@@ -63,10 +71,10 @@ module I18n
63
71
 
64
72
  keys = I18n::Gettext.plural_keys(locale)
65
73
  values = value.split("\000")
66
- raise "invalid number of plurals: #{values.size}, keys: #{keys.inspect}" if values.size != keys.size
74
+ raise "invalid number of plurals: #{values.size}, keys: #{keys.inspect} on #{locale} locale for msgid #{key.inspect} with values #{values.inspect}" if values.size != keys.size
67
75
 
68
76
  result = {}
69
- values.each_with_index { |value, ix| result[keys[ix]] = value }
77
+ values.each_with_index { |_value, ix| result[keys[ix]] = _value }
70
78
  [key, result]
71
79
  end
72
80
 
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  # The InterpolationCompiler module contains optimizations that can tremendously
4
4
  # speed up the interpolation process on the Simple backend.
@@ -10,19 +10,18 @@
10
10
  # To enable pre-compiled interpolations you can simply include the
11
11
  # InterpolationCompiler module to the Simple backend:
12
12
  #
13
- # I18n::Backend::Simple.send(:include, I18n::Backend::InterpolationCompiler)
13
+ # I18n::Backend::Simple.include(I18n::Backend::InterpolationCompiler)
14
14
  #
15
15
  # Note that InterpolationCompiler does not yield meaningful results and consequently
16
16
  # should not be used with Ruby 1.9 (YARV) but improves performance everywhere else
17
- # (jRuby, Rubinius and 1.8.7).
17
+ # (jRuby, Rubinius).
18
18
  module I18n
19
19
  module Backend
20
20
  module InterpolationCompiler
21
21
  module Compiler
22
22
  extend self
23
23
 
24
- TOKENIZER = /(%%\{[^\}]+\}|%\{[^\}]+\})/
25
- INTERPOLATION_SYNTAX_PATTERN = /(%)?(%\{([^\}]+)\})/
24
+ TOKENIZER = /(%%?\{[^}]+\})/
26
25
 
27
26
  def compile_if_an_interpolation(string)
28
27
  if interpolated_str?(string)
@@ -37,7 +36,7 @@ module I18n
37
36
  end
38
37
 
39
38
  def interpolated_str?(str)
40
- str.kind_of?(::String) && str =~ INTERPOLATION_SYNTAX_PATTERN
39
+ str.kind_of?(::String) && str =~ TOKENIZER
41
40
  end
42
41
 
43
42
  protected
@@ -48,13 +47,12 @@ module I18n
48
47
 
49
48
  def compiled_interpolation_body(str)
50
49
  tokenize(str).map do |token|
51
- (matchdata = token.match(INTERPOLATION_SYNTAX_PATTERN)) ? handle_interpolation_token(token, matchdata) : escape_plain_str(token)
50
+ token.match(TOKENIZER) ? handle_interpolation_token(token) : escape_plain_str(token)
52
51
  end.join
53
52
  end
54
53
 
55
- def handle_interpolation_token(interpolation, matchdata)
56
- escaped, pattern, key = matchdata.values_at(1, 2, 3)
57
- escaped ? pattern : compile_interpolation_token(key.to_sym)
54
+ def handle_interpolation_token(token)
55
+ token.start_with?('%%') ? token[1..] : compile_interpolation_token(token[2..-2])
58
56
  end
59
57
 
60
58
  def compile_interpolation_token(key)
@@ -63,7 +61,7 @@ module I18n
63
61
 
64
62
  def interpolate_or_raise_missing(key)
65
63
  escaped_key = escape_key_sym(key)
66
- Base::RESERVED_KEYS.include?(key) ? reserved_key(escaped_key) : interpolate_key(escaped_key)
64
+ RESERVED_KEYS.include?(key) ? reserved_key(escaped_key) : interpolate_key(escaped_key)
67
65
  end
68
66
 
69
67
  def interpolate_key(key)
@@ -79,7 +77,7 @@ module I18n
79
77
  end
80
78
 
81
79
  def missing_key(key)
82
- "raise(MissingInterpolationArgument.new(#{key}, self))"
80
+ "I18n.config.missing_interpolation_argument_handler.call(#{key}, v, self)"
83
81
  end
84
82
 
85
83
  def reserved_key(key)
@@ -106,7 +104,7 @@ module I18n
106
104
  end
107
105
  end
108
106
 
109
- def store_translations(locale, data, options = {})
107
+ def store_translations(locale, data, options = EMPTY_HASH)
110
108
  compile_all_strings_in(data)
111
109
  super
112
110
  end
@@ -120,4 +118,4 @@ module I18n
120
118
  end
121
119
  end
122
120
  end
123
- end
121
+ end