svenfuchs-i18n 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,57 @@
1
+ h1. Changelog
2
+
3
+ h2. master
4
+
5
+ * (no changes)
6
+
7
+ h2. 0.2.0 (2009-07-12, "fb9819c7efd3713c178c47b0722d0b39fff14131":http://github.com/svenfuchs/i18n/commit/fb9819c7efd3713c178c47b0722d0b39fff14131)
8
+
9
+ * "Allow using Ruby 1.9 syntax for string interpolation (API addition)":http://github.com/svenfuchs/i18n/commit/c6e0b06d512f2af57199a843a1d8a40241b32861
10
+ * "Allow configuring the default scope separator, allow to pass a custom scope separator (e.g. I18n.t(:'foo|bar', :separator => '|') (API addition)":http://github.com/svenfuchs/i18n/commit/5b75bfbc348061adc11e3790187a187275bfd471
11
+ * "Pass :format option to #translate for #localize more useful lambda support":http://github.com/svenfuchs/i18n/commit/e277711b3c844fe7589b8d3f9af0f7d1b969a273
12
+ * "Refactor Simple backend #resolve to #default and #resolve for more consistency. Now allows to pass lambdas as defaults and re-resolve Symbols":http://github.com/svenfuchs/i18n/commit/8c4ce3d923ce5fa73e973fe28217e18165549aba
13
+ * "Add lambda support to #translate (API addition)":http://github.com/svenfuchs/i18n/commit/c90e62d8f7d3d5b78f34cfe328d871b58884f115
14
+ * "Add lambda support to #localize (API addition)":http://github.com/svenfuchs/i18n/commit/9d390afcf33f3f469bb95e6888147152f6cc7442
15
+
16
+ h2. 0.1.3 (2009-02-27)
17
+
18
+ * "Remove unnecessary string encoding handling in the i18n simple backend which made the backend break on Ruby 1.9":http://github.com/svenfuchs/i18n/commit/4c3a970783861a94f2e89f46714fb3434e4f4f8d
19
+
20
+ h2. 0.1.2 (2009-01-09)
21
+
22
+ * "added #available_locales (returns an array of locales for which translations are available)":http://github.com/svenfuchs/i18n/commit/411f8fe7c8f3f89e9b6b921fa62ed66cb92f3af4
23
+ * "flatten load_path before using it so that a nested array of paths won't throw up":http://github.com/svenfuchs/i18n/commit/d473a068a2b90aba98135deb225d6eb6d8104d70
24
+
25
+ h2. 0.1.1 (2008-11-20)
26
+
27
+ * "Use :'en' as a default locale (in favor of :'en-US')":http://github.com/svenfuchs/i18n/commit/c4b10b246aecf7da78cb2568dd0d2ab7e6b8a230
28
+ * "Add #reload! to Simple backend":http://github.com/svenfuchs/i18n/commit/36dd2bd9973b9e1559728749a9daafa44693e964
29
+
30
+ h2. 0.1.0 (2008-10-25)
31
+
32
+ * "Fix Simple backend to distinguish false from nil values":http://github.com/svenfuchs/i18n/commit/39d9a47da14b5f3ba126af48923af8c30e135166
33
+ * "Add #load_path to public api, add initialize to simple backend and remove #load_translations from public api":http://github.com/svenfuchs/i18n/commit/c4c5649e6bc8f020f1aaf5a5470bde048e22c82d
34
+ * "Speed up Backend::Simple#interpolate":http://github.com/svenfuchs/i18n/commit/9e1ac6bf8833304e036323ec9932b9f33c468a35
35
+ * "Remove #populate and #store_translations from public API":http://github.com/svenfuchs/i18n/commit/f4e514a80be7feb509f66824ee311905e2940900
36
+ * "Use :other instead of :many as a plural key":http://github.com/svenfuchs/i18n/commit/0f8f20a2552bf6a2aa758d8fdd62a7154e4a1bf6
37
+ * "Use a class instead of a module for Simple backend":http://github.com/svenfuchs/i18n/commit/08f051aa61320c17debde24a83268bc74e33b995
38
+ * "Make Simple backend #interpolate deal with non-ASCII string encodings":http://github.com/svenfuchs/i18n/commit/d84a3f3f55543c084d5dc5d1fed613b8df148789
39
+ * "Fix default arrays of non-existant keys returning the default array":http://github.com/svenfuchs/i18n/commit/6c04ca86c87f97dc78f07c2a4023644e5ba8b839
40
+
41
+ h2. Initial implementation (June/July 2008)
42
+
43
+ Initial implementation by "Sven Fuchs":http://www.workingwithrails.com/person/9963-sven-fuchs based on previous discussion/consensus of the rails-i18n team (alphabetical order) and many others:
44
+
45
+ * "Matt Aimonetti":http://railsontherun.com
46
+ * "Sven Fuchs":http://www.workingwithrails.com/person/9963-sven-fuchs
47
+ * "Joshua Harvey":http://www.workingwithrails.com/person/759-joshua-harvey
48
+ * "Saimon Moore":http://saimonmoore.net
49
+ * "Stephan Soller":http://www.arkanis-development.de
50
+
51
+ h2. More information
52
+
53
+ * "Homepage":http://rails-i18n.org
54
+ * "Wiki":http://rails-i18n.org/wiki
55
+ * "Mailinglist":http://groups.google.com/group/rails-i18n
56
+ * "About the project/history":http://www.artweb-design.de/2008/7/18/finally-ruby-on-rails-gets-internationalized
57
+ * "Initial API Intro":http://www.artweb-design.de/2008/7/18/the-ruby-on-rails-i18n-core-api
@@ -0,0 +1,20 @@
1
+ task :default => [:test]
2
+
3
+ task :test do
4
+ ruby "test/all.rb"
5
+ end
6
+
7
+ begin
8
+ require 'jeweler'
9
+ Jeweler::Tasks.new do |s|
10
+ s.name = "i18n"
11
+ s.summary = "New wave Internationalization support for Ruby"
12
+ s.email = "rails-i18n@googlegroups.com"
13
+ s.homepage = "http://rails-i18n.org"
14
+ s.description = "Add Internationalization support to your Ruby application."
15
+ s.authors = ['Sven Fuchs', 'Joshua Harvey', 'Matt Aimonetti', 'Stephan Soller', 'Saimon Moore']
16
+ s.files = FileList["[A-Z]*", "{lib,test}/**/*"]
17
+ end
18
+ rescue LoadError
19
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
20
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -7,11 +7,13 @@
7
7
  # License:: MIT
8
8
  require 'i18n/backend/simple'
9
9
  require 'i18n/exceptions'
10
+ require 'i18n/string'
10
11
 
11
12
  module I18n
12
13
  @@backend = nil
13
14
  @@load_path = nil
14
15
  @@default_locale = :'en'
16
+ @@default_separator = '.'
15
17
  @@exception_handler = :default_exception_handler
16
18
 
17
19
  class << self
@@ -50,6 +52,16 @@ module I18n
50
52
  backend.available_locales
51
53
  end
52
54
 
55
+ # Returns the current default scope separator. Defaults to '.'
56
+ def default_separator
57
+ @@default_separator
58
+ end
59
+
60
+ # Sets the current default scope separator.
61
+ def default_separator=(separator)
62
+ @@default_separator = separator
63
+ end
64
+
53
65
  # Sets the exception handler.
54
66
  def exception_handler=(exception_handler)
55
67
  @@exception_handler = exception_handler
@@ -150,7 +162,7 @@ module I18n
150
162
  # or <tt>default</tt> if no translations for <tt>:foo</tt> and <tt>:bar</tt> were found.
151
163
  # I18n.t :foo, :default => [:bar, 'default']
152
164
  #
153
- # <b>BULK LOOKUP</b>
165
+ # *BULK LOOKUP*
154
166
  #
155
167
  # This returns an array with the translations for <tt>:foo</tt> and <tt>:bar</tt>.
156
168
  # I18n.t [:foo, :bar]
@@ -160,8 +172,26 @@ module I18n
160
172
  #
161
173
  # Which is the same as using a scope option:
162
174
  # I18n.t [:foo, :bar], :scope => :baz
163
- def translate(key, options = {})
164
- locale = options.delete(:locale) || I18n.locale
175
+ #
176
+ # *LAMBDAS*
177
+ #
178
+ # Both translations and defaults can be given as Ruby lambdas. Lambdas will be
179
+ # called and passed the key and options.
180
+ #
181
+ # E.g. assuming the key <tt>:salutation</tt> resolves to:
182
+ # lambda { |key, options| options[:gender] == 'm' ? "Mr. {{options[:name]}}" : "Mrs. {{options[:name]}}" }
183
+ #
184
+ # Then <tt>I18n.t(:salutation, :gender => 'w', :name => 'Smith') will result in "Mrs. Smith".
185
+ #
186
+ # It is recommended to use/implement lambdas in an "idempotent" way. E.g. when
187
+ # a cache layer is put in front of I18n.translate it will generate a cache key
188
+ # from the argument values passed to #translate. Therefor your lambdas should
189
+ # always return the same translations/values per unique combination of argument
190
+ # values.
191
+ def translate(*args)
192
+ options = args.last.is_a?(Hash) ? args.pop : {}
193
+ key = args.shift
194
+ locale = options.delete(:locale) || I18n.locale
165
195
  backend.translate(locale, key, options)
166
196
  rescue I18n::ArgumentError => e
167
197
  raise e if options[:raise]
@@ -190,9 +220,9 @@ module I18n
190
220
  # Merges the given locale, key and scope into a single array of keys.
191
221
  # Splits keys that contain dots into multiple keys. Makes sure all
192
222
  # keys are Symbols.
193
- def normalize_translation_keys(locale, key, scope)
194
- keys = [locale] + Array(scope) + [key]
195
- keys = keys.map { |k| k.to_s.split(/\./) }
223
+ def normalize_translation_keys(locale, key, scope, separator = nil)
224
+ keys = [locale] + Array(scope) + Array(key)
225
+ keys = keys.map { |k| k.to_s.split(separator || I18n.default_separator) }
196
226
  keys.flatten.map { |k| k.to_sym }
197
227
  end
198
228
  end
@@ -3,8 +3,8 @@ require 'yaml'
3
3
  module I18n
4
4
  module Backend
5
5
  class Simple
6
- INTERPOLATION_RESERVED_KEYS = %w(scope default)
7
- MATCH = /(\\\\)?\{\{([^\}]+)\}\}/
6
+ RESERVED_KEYS = [:scope, :default, :separator]
7
+ INTERPOLATION_SYNTAX_PATTERN = /(\\\\)?\{\{([^\}]+)\}\}/
8
8
 
9
9
  # Accepts a list of paths to translation files. Loads translations from
10
10
  # plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml
@@ -23,20 +23,15 @@ module I18n
23
23
 
24
24
  def translate(locale, key, options = {})
25
25
  raise InvalidLocale.new(locale) if locale.nil?
26
- return key.map { |k| translate(locale, k, options) } if key.is_a? Array
27
-
28
- reserved = :scope, :default
29
- count, scope, default = options.values_at(:count, *reserved)
30
- options.delete(:default)
31
- values = options.reject { |name, value| reserved.include?(name) }
32
-
33
- entry = lookup(locale, key, scope)
34
- if entry.nil?
35
- entry = default(locale, default, options)
36
- if entry.nil?
37
- raise(I18n::MissingTranslationData.new(locale, key, options))
38
- end
39
- end
26
+ return key.map { |k| translate(locale, k, options) } if key.is_a?(Array)
27
+
28
+ count, scope, default, separator = options.values_at(:count, *RESERVED_KEYS)
29
+ values = options.reject { |name, value| RESERVED_KEYS.include?(name) }
30
+
31
+ entry = lookup(locale, key, scope, separator)
32
+ entry = entry.nil? ? default(locale, key, default, options) : resolve(locale, key, entry, options)
33
+
34
+ raise(I18n::MissingTranslationData.new(locale, key, options)) if entry.nil?
40
35
  entry = pluralize(locale, entry, count)
41
36
  entry = interpolate(locale, entry, values)
42
37
  entry
@@ -45,23 +40,23 @@ module I18n
45
40
  # Acts the same as +strftime+, but returns a localized version of the
46
41
  # formatted date string. Takes a key from the date/time formats
47
42
  # translations as a format argument (<em>e.g.</em>, <tt>:short</tt> in <tt>:'date.formats'</tt>).
48
- def localize(locale, object, format = :default)
43
+ def localize(locale, object, format = :default, options={})
49
44
  raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
50
45
 
51
- type = object.respond_to?(:sec) ? 'time' : 'date'
52
- # TODO only translate these if format is a String?
53
- formats = translate(locale, :"#{type}.formats")
54
- format = formats[format.to_sym] if formats && formats[format.to_sym]
55
- # TODO raise exception unless format found?
56
- format = format.to_s.dup
57
-
58
- # TODO only translate these if the format string is actually present
59
- # TODO check which format strings are present, then bulk translate then, then replace them
60
- format.gsub!(/%a/, translate(locale, :"date.abbr_day_names")[object.wday])
61
- format.gsub!(/%A/, translate(locale, :"date.day_names")[object.wday])
62
- format.gsub!(/%b/, translate(locale, :"date.abbr_month_names")[object.mon])
63
- format.gsub!(/%B/, translate(locale, :"date.month_names")[object.mon])
64
- format.gsub!(/%p/, translate(locale, :"time.#{object.hour < 12 ? :am : :pm}")) if object.respond_to? :hour
46
+ if Symbol === format
47
+ type = object.respond_to?(:sec) ? 'time' : 'date'
48
+ format = lookup(locale, :"#{type}.formats.#{format}")
49
+ end
50
+
51
+ format = resolve(locale, object, format, options.merge(:raise => true))
52
+
53
+ # TODO check which format strings are present, then bulk translate them, then replace them
54
+ format.gsub!(/%a/, translate(locale, :"date.abbr_day_names", :format => format)[object.wday]) if format.include?('%a')
55
+ format.gsub!(/%A/, translate(locale, :"date.day_names", :format => format)[object.wday]) if format.include?('%A')
56
+ format.gsub!(/%b/, translate(locale, :"date.abbr_month_names", :format => format)[object.mon]) if format.include?('%b')
57
+ format.gsub!(/%B/, translate(locale, :"date.month_names", :format => format)[object.mon]) if format.include?('%B')
58
+ format.gsub!(/%p/, translate(locale, :"time.#{object.hour < 12 ? :am : :pm}", :format => format)) if format.include?('%p') && object.respond_to?(:hour)
59
+
65
60
  object.strftime(format)
66
61
  end
67
62
 
@@ -95,10 +90,10 @@ module I18n
95
90
  # nested translations hash. Splits keys or scopes containing dots
96
91
  # into multiple keys, i.e. <tt>currency.format</tt> is regarded the same as
97
92
  # <tt>%w(currency format)</tt>.
98
- def lookup(locale, key, scope = [])
93
+ def lookup(locale, key, scope = [], separator = nil)
99
94
  return unless key
100
95
  init_translations unless initialized?
101
- keys = I18n.send(:normalize_translation_keys, locale, key, scope)
96
+ keys = I18n.send(:normalize_translation_keys, locale, key, scope, separator)
102
97
  keys.inject(translations) do |result, k|
103
98
  if (x = result[k.to_sym]).nil?
104
99
  return nil
@@ -108,20 +103,34 @@ module I18n
108
103
  end
109
104
  end
110
105
 
111
- # Evaluates a default translation.
112
- # If the given default is a String it is used literally. If it is a Symbol
113
- # it will be translated with the given options. If it is an Array the first
114
- # translation yielded will be returned.
115
- #
116
- # <em>I.e.</em>, <tt>default(locale, [:foo, 'default'])</tt> will return +default+ if
117
- # <tt>translate(locale, :foo)</tt> does not yield a result.
118
- def default(locale, default, options = {})
119
- case default
120
- when String then default
121
- when Symbol then translate locale, default, options
122
- when Array then default.each do |obj|
123
- result = default(locale, obj, options.dup) and return result
106
+ # Evaluates defaults.
107
+ # If given subject is an Array, it walks the array and returns the
108
+ # first translation that can be resolved. Otherwise it tries to resolve
109
+ # the translation directly.
110
+ def default(locale, object, subject, options = {})
111
+ options = options.dup.reject { |key, value| key == :default }
112
+ case subject
113
+ when Array
114
+ subject.each do |subject|
115
+ result = resolve(locale, object, subject, options) and return result
124
116
  end and nil
117
+ else
118
+ resolve(locale, object, subject, options)
119
+ end
120
+ end
121
+
122
+ # Resolves a translation.
123
+ # If the given subject is a Symbol, it will be translated with the
124
+ # given options. If it is a Proc then it will be evaluated. All other
125
+ # subjects will be returned directly.
126
+ def resolve(locale, object, subject, options = {})
127
+ case subject
128
+ when Symbol
129
+ translate(locale, subject, options)
130
+ when Proc
131
+ resolve(locale, object, subject.call(object, options), options = {})
132
+ else
133
+ subject
125
134
  end
126
135
  rescue MissingTranslationData
127
136
  nil
@@ -133,7 +142,7 @@ module I18n
133
142
  # implement more flexible or complex pluralization rules.
134
143
  def pluralize(locale, entry, count)
135
144
  return entry unless entry.is_a?(Hash) and count
136
- # raise InvalidPluralizationData.new(entry, count) unless entry.is_a?(Hash)
145
+
137
146
  key = :zero if count == 0 && entry.has_key?(:zero)
138
147
  key ||= count == 1 ? :one : :other
139
148
  raise InvalidPluralizationData.new(entry, count) unless entry.has_key?(key)
@@ -149,21 +158,22 @@ module I18n
149
158
  # the <tt>{{...}}</tt> key in a string (once for the string and once for the
150
159
  # interpolation).
151
160
  def interpolate(locale, string, values = {})
152
- return string unless string.is_a?(String)
161
+ return string unless string.is_a?(String) && !values.empty?
153
162
 
154
- string.gsub(MATCH) do
155
- escaped, pattern, key = $1, $2, $2.to_sym
163
+ string.gsub(INTERPOLATION_SYNTAX_PATTERN) do
164
+ escaped, key = $1, $2.to_sym
156
165
 
157
166
  if escaped
158
- pattern
159
- elsif INTERPOLATION_RESERVED_KEYS.include?(pattern)
160
- raise ReservedInterpolationKey.new(pattern, string)
161
- elsif !values.include?(key)
162
- raise MissingInterpolationArgument.new(pattern, string)
167
+ key
168
+ elsif RESERVED_KEYS.include?(key)
169
+ raise ReservedInterpolationKey.new(key, string)
163
170
  else
164
- values[key].to_s
171
+ "%{#{key}}"
165
172
  end
166
- end
173
+ end % values
174
+
175
+ rescue KeyError => e
176
+ raise MissingInterpolationArgument.new(values, string)
167
177
  end
168
178
 
169
179
  # Loads a single translations file by delegating to #load_rb or
@@ -204,11 +214,20 @@ module I18n
204
214
  # Return a new hash with all keys and nested keys converted to symbols.
205
215
  def deep_symbolize_keys(hash)
206
216
  hash.inject({}) { |result, (key, value)|
207
- value = deep_symbolize_keys(value) if value.is_a? Hash
217
+ value = deep_symbolize_keys(value) if value.is_a?(Hash)
208
218
  result[(key.to_sym rescue key) || key] = value
209
219
  result
210
220
  }
211
221
  end
222
+
223
+ # Flatten the given array once
224
+ def flatten_once(array)
225
+ result = []
226
+ for element in array # a little faster than each
227
+ result.push(*element)
228
+ end
229
+ result
230
+ end
212
231
  end
213
232
  end
214
233
  end
@@ -1,3 +1,9 @@
1
+ class KeyError < IndexError
2
+ def initialize(message = nil)
3
+ super(message || "key not found")
4
+ end
5
+ end unless defined?(KeyError)
6
+
1
7
  module I18n
2
8
  class ArgumentError < ::ArgumentError; end
3
9
 
@@ -28,10 +34,10 @@ module I18n
28
34
  end
29
35
 
30
36
  class MissingInterpolationArgument < ArgumentError
31
- attr_reader :key, :string
32
- def initialize(key, string)
33
- @key, @string = key, string
34
- super "interpolation argument #{key} missing in #{string.inspect}"
37
+ attr_reader :values, :string
38
+ def initialize(values, string)
39
+ @values, @string = values, string
40
+ super "missing interpolation argument in #{string.inspect} (#{values.inspect} given)"
35
41
  end
36
42
  end
37
43
 
@@ -0,0 +1,93 @@
1
+ =begin
2
+ heavily based on Masao Mutoh's gettext String interpolation extension
3
+ http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb
4
+ Copyright (C) 2005-2009 Masao Mutoh
5
+ You may redistribute it and/or modify it under the same license terms as Ruby.
6
+ =end
7
+
8
+ if RUBY_VERSION < '1.9'
9
+
10
+ # KeyError is raised by String#% when the string contains a named placeholder
11
+ # that is not contained in the given arguments hash. Ruby 1.9 includes and
12
+ # raises this exception natively. We define it to mimic Ruby 1.9's behaviour
13
+ # in Ruby 1.8.x
14
+
15
+ class KeyError < IndexError
16
+ def initialize(message = nil)
17
+ super(message || "key not found")
18
+ end
19
+ end unless defined?(KeyError)
20
+
21
+ # Extension for String class. This feature is included in Ruby 1.9 or later but not occur TypeError.
22
+ #
23
+ # String#% method which accept "named argument". The translator can know
24
+ # the meaning of the msgids using "named argument" instead of %s/%d style.
25
+
26
+ class String
27
+ # For older ruby versions, such as ruby-1.8.5
28
+ alias :bytesize :size unless instance_methods.find {|m| m.to_s == 'bytesize'}
29
+ alias :interpolate_without_ruby_19_syntax :% # :nodoc:
30
+
31
+ INTERPOLATION_PATTERN = Regexp.union(
32
+ /%%/,
33
+ /%\{(\w+)\}/, # matches placeholders like "%{foo}"
34
+ /%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
35
+ )
36
+
37
+ # % uses self (i.e. the String) as a format specification and returns the
38
+ # result of applying it to the given arguments. In other words it interpolates
39
+ # the given arguments to the string according to the formats the string
40
+ # defines.
41
+ #
42
+ # There are three ways to use it:
43
+ #
44
+ # * Using a single argument or Array of arguments.
45
+ #
46
+ # This is the default behaviour of the String class. See Kernel#sprintf for
47
+ # more details about the format string.
48
+ #
49
+ # Example:
50
+ #
51
+ # "%d %s" % [1, "message"]
52
+ # # => "1 message"
53
+ #
54
+ # * Using a Hash as an argument and unformatted, named placeholders.
55
+ #
56
+ # When you pass a Hash as an argument and specify placeholders with %{foo}
57
+ # it will interpret the hash values as named arguments.
58
+ #
59
+ # Example:
60
+ #
61
+ # "%{firstname}, %{lastname}" % {:firstname => "Masao", :lastname => "Mutoh"}
62
+ # # => "Masao Mutoh"
63
+ #
64
+ # * Using a Hash as an argument and formatted, named placeholders.
65
+ #
66
+ # When you pass a Hash as an argument and specify placeholders with %<foo>d
67
+ # it will interpret the hash values as named arguments and format the value
68
+ # according to the formatting instruction appended to the closing >.
69
+ #
70
+ # Example:
71
+ #
72
+ # "%<integer>d, %<float>.1f" % { :integer => 10, :float => 43.4 }
73
+ # # => "10, 43.3"
74
+ def %(args)
75
+ if args.kind_of?(Hash)
76
+ dup.gsub(INTERPOLATION_PATTERN) do |match|
77
+ if match == '%%'
78
+ '%'
79
+ else
80
+ key = ($1 || $2).to_sym
81
+ raise KeyError unless args.has_key?(key)
82
+ $3 ? sprintf("%#{$3}", args[key]) : args[key]
83
+ end
84
+ end
85
+ elsif self =~ INTERPOLATION_PATTERN
86
+ raise ArgumentError.new('one hash required')
87
+ else
88
+ result = gsub(/%([{<])/, '%%\1')
89
+ result.send :'interpolate_without_ruby_19_syntax', args
90
+ end
91
+ end
92
+ end
93
+ end