i18n 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of i18n might be problematic. Click here for more details.
- data/CHANGELOG.textile +9 -1
- data/README.textile +24 -1
- data/lib/i18n.rb +15 -6
- data/lib/i18n/backend/active_record/missing.rb +7 -7
- data/lib/i18n/backend/active_record/translation.rb +36 -19
- data/lib/i18n/backend/base.rb +29 -51
- data/lib/i18n/backend/cache.rb +43 -16
- data/lib/i18n/backend/chain.rb +45 -40
- data/lib/i18n/backend/fallbacks.rb +10 -7
- data/lib/i18n/backend/gettext.rb +1 -3
- data/lib/i18n/backend/simple.rb +5 -5
- data/lib/i18n/config.rb +6 -4
- data/lib/i18n/exceptions.rb +11 -1
- data/lib/i18n/gettext/helpers.rb +1 -1
- data/lib/i18n/version.rb +1 -1
- metadata +7 -7
data/CHANGELOG.textile
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
h1. Changelog
|
2
2
|
|
3
|
-
h2.
|
3
|
+
h2. 0.4.2 (2010-10-26)
|
4
|
+
|
5
|
+
* "Improve UTF8 handling":http://github.com/svenfuchs/i18n/commit/e8d5820a3b08eeca28de1a2b9c8a6ad2b9e6476c
|
6
|
+
* "Expose I18n::VERSION":http://github.com/svenfuchs/i18n/commit/b832037bac94c7144f45f3ff5e3b4e4089781726
|
7
|
+
* "Better deprecation output":http://github.com/svenfuchs/i18n/commit/2bee924464b8a9c33d3d7852eb1c8423aa38cc25
|
8
|
+
|
9
|
+
h2. 0.4.1 (2010-06-05)
|
10
|
+
|
11
|
+
* "Fix interpolation failure on Ruby 1.9":http://github.com/svenfuchs/i18n/commit/8d45bedb11c4136c00e853d104b00a8e67ec4894
|
4
12
|
|
5
13
|
h2. 0.4.0 (2010-05-27)
|
6
14
|
|
data/README.textile
CHANGED
@@ -29,12 +29,16 @@ Alternative backends:
|
|
29
29
|
* ActiveRecord (optionally: ActiveRecord::Missing and ActiveRecord::StoreProcs)
|
30
30
|
* KeyValue (uses active_support/json and cannot store procs)
|
31
31
|
|
32
|
-
For more information and lots of resources see: "http://
|
32
|
+
For more information and lots of resources see: "http://ruby-i18n.org/wiki":http://ruby-i18n.org/wiki
|
33
33
|
|
34
34
|
h2. Installation
|
35
35
|
|
36
36
|
gem install i18n
|
37
37
|
|
38
|
+
h4. Rails version warning
|
39
|
+
|
40
|
+
On Rails < 2.3.6 the method I18n.localize will fail with MissingInterpolationArgument (issue "20":http://github.com/svenfuchs/i18n/issues/issue/20). Upgrade to Rails 2.3.6 or higher (2.3.8 preferably) is recommended.
|
41
|
+
|
38
42
|
h3. Installation on Rails < 2.3.5 (deprecated)
|
39
43
|
|
40
44
|
Up to version 2.3.4 Rails will not accept i18n gems > 0.1.3. There is an unpacked
|
@@ -43,6 +47,7 @@ This requirement is relaxed in "6da03653":http://github.com/rails/rails/commit/6
|
|
43
47
|
|
44
48
|
The new i18n gem can be loaded from vendor/plugins like this:
|
45
49
|
|
50
|
+
<pre>
|
46
51
|
def reload_i18n!
|
47
52
|
raise "Move to i18n version 0.2.0 or greater" if Rails.version > "2.3.4"
|
48
53
|
|
@@ -50,6 +55,7 @@ The new i18n gem can be loaded from vendor/plugins like this:
|
|
50
55
|
I18n::Backend.send :remove_const, "Simple"
|
51
56
|
$: << Rails.root.join('vendor', 'plugins', 'i18n', 'lib').to_s
|
52
57
|
end
|
58
|
+
</pre>
|
53
59
|
|
54
60
|
Then you can `reload_i18n!` inside an i18n initializer.
|
55
61
|
|
@@ -61,6 +67,23 @@ You can run tests both with
|
|
61
67
|
* run any test file directly, e.g. `ruby test/api/simple_test.rb`
|
62
68
|
* run all tests with `ruby test/all.rb`
|
63
69
|
|
70
|
+
You can parametrize the test suite for using different sets of dependencies by
|
71
|
+
using:
|
72
|
+
|
73
|
+
.pre `ruby test/all.rb -w DEPENDENCIES`
|
74
|
+
|
75
|
+
... where DEPENDENCIES is a comma-separated list of:
|
76
|
+
|
77
|
+
* r23 or rails-2.3.x
|
78
|
+
* r3 or rails-3.x
|
79
|
+
* no-rails
|
80
|
+
* sqlite
|
81
|
+
* mysql
|
82
|
+
|
83
|
+
So, e.g. this would run the test suite against Rails 2.3.x using mysql:
|
84
|
+
|
85
|
+
.pre `ruby test/all.rb -w r23,mysql`
|
86
|
+
|
64
87
|
The structure of the test suite is a bit unusual as it uses modules to reuse
|
65
88
|
particular tests in different test cases.
|
66
89
|
|
data/lib/i18n.rb
CHANGED
@@ -7,6 +7,7 @@
|
|
7
7
|
# Matt Aimonetti (http://railsontherun.com/)
|
8
8
|
# Copyright:: Copyright (c) 2008 The Ruby i18n Team
|
9
9
|
# License:: MIT
|
10
|
+
require 'i18n/version'
|
10
11
|
require 'i18n/exceptions'
|
11
12
|
require 'i18n/core_ext/string/interpolate'
|
12
13
|
|
@@ -145,19 +146,27 @@ module I18n
|
|
145
146
|
# always return the same translations/values per unique combination of argument
|
146
147
|
# values.
|
147
148
|
def translate(*args)
|
148
|
-
options = args.
|
149
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
149
150
|
key = args.shift
|
150
|
-
|
151
|
-
|
152
|
-
|
151
|
+
backend = config.backend
|
152
|
+
locale = options.delete(:locale) || config.locale
|
153
|
+
raises = options.delete(:raise)
|
154
|
+
|
155
|
+
raise I18n::ArgumentError if key.is_a?(String) && key.empty?
|
156
|
+
|
157
|
+
if key.is_a?(Array)
|
158
|
+
key.map { |k| backend.translate(locale, k, options) }
|
159
|
+
else
|
160
|
+
backend.translate(locale, key, options)
|
161
|
+
end
|
153
162
|
rescue I18n::ArgumentError => exception
|
154
163
|
raise exception if raises
|
155
164
|
handle_exception(exception, locale, key, options)
|
156
165
|
end
|
157
166
|
alias :t :translate
|
158
167
|
|
159
|
-
def translate!(key, options
|
160
|
-
translate(key, options.merge(
|
168
|
+
def translate!(key, options={})
|
169
|
+
translate(key, options.merge(:raise => true))
|
161
170
|
end
|
162
171
|
alias :t! :translate!
|
163
172
|
|
@@ -8,7 +8,7 @@
|
|
8
8
|
# Example usage:
|
9
9
|
#
|
10
10
|
# I18n::Backend::Chain.send(:include, I18n::Backend::ActiveRecord::Missing)
|
11
|
-
# I18n.backend =
|
11
|
+
# I18n.backend = I18n::Backend::Chain.new(I18n::Backend::ActiveRecord.new, I18n::Backend::Simple.new)
|
12
12
|
#
|
13
13
|
# Stub records for pluralizations will also be created for each key defined
|
14
14
|
# in i18n.plural.keys.
|
@@ -33,16 +33,16 @@ module I18n
|
|
33
33
|
module Backend
|
34
34
|
class ActiveRecord
|
35
35
|
module Missing
|
36
|
+
include Flatten
|
37
|
+
|
36
38
|
def store_default_translations(locale, key, options = {})
|
37
|
-
count, scope, default, separator = options.values_at(:count,
|
39
|
+
count, scope, default, separator = options.values_at(:count, :scope, :default, :separator)
|
38
40
|
separator ||= I18n.default_separator
|
39
|
-
|
40
|
-
keys = I18n.normalize_keys(locale, key, scope, separator)[1..-1]
|
41
|
-
key = keys.join(separator || I18n.default_separator)
|
41
|
+
key = normalize_flat_keys(locale, key, scope, separator)
|
42
42
|
|
43
43
|
unless ActiveRecord::Translation.locale(locale).lookup(key).exists?
|
44
|
-
interpolations = options.
|
45
|
-
keys = count ? I18n.t('i18n.plural.keys', :locale => locale).map { |k| [key, k].join(
|
44
|
+
interpolations = options.keys - Base::RESERVED_KEYS
|
45
|
+
keys = count ? I18n.t('i18n.plural.keys', :locale => locale).map { |k| [key, k].join(FLATTEN_SEPARATOR) } : [key]
|
46
46
|
keys.each { |key| store_default_translation(locale, key, interpolations) }
|
47
47
|
end
|
48
48
|
end
|
@@ -46,33 +46,36 @@ module I18n
|
|
46
46
|
# # => 'FOO'
|
47
47
|
class ActiveRecord
|
48
48
|
class Translation < ::ActiveRecord::Base
|
49
|
+
TRUTHY_CHAR = "\001"
|
50
|
+
FALSY_CHAR = "\002"
|
51
|
+
|
49
52
|
set_table_name 'translations'
|
50
53
|
attr_protected :is_proc, :interpolations
|
51
54
|
|
52
55
|
serialize :value
|
53
56
|
serialize :interpolations, Array
|
54
57
|
|
55
|
-
|
58
|
+
class << self
|
59
|
+
def locale(locale)
|
60
|
+
scoped(:conditions => { :locale => locale.to_s })
|
61
|
+
end
|
56
62
|
|
57
|
-
|
58
|
-
|
59
|
-
|
63
|
+
def lookup(keys, *separator)
|
64
|
+
column_name = connection.quote_column_name('key')
|
65
|
+
keys = Array(keys).map! { |key| key.to_s }
|
60
66
|
|
61
|
-
|
62
|
-
|
63
|
-
|
67
|
+
unless separator.empty?
|
68
|
+
warn "[DEPRECATION] Giving a separator to Translation.lookup is deprecated. " <<
|
69
|
+
"You can change the internal separator by overwriting FLATTEN_SEPARATOR."
|
70
|
+
end
|
64
71
|
|
65
|
-
|
66
|
-
|
67
|
-
"You can change the internal separator by overwriting FLATTEN_SEPARATOR."
|
72
|
+
namespace = "#{keys.last}#{I18n::Backend::Flatten::FLATTEN_SEPARATOR}%"
|
73
|
+
scoped(:conditions => ["#{column_name} IN (?) OR #{column_name} LIKE ?", keys, namespace])
|
68
74
|
end
|
69
75
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
def self.available_locales
|
75
|
-
Translation.find(:all, :select => 'DISTINCT locale').map { |t| t.locale.to_sym }
|
76
|
+
def available_locales
|
77
|
+
Translation.find(:all, :select => 'DISTINCT locale').map { |t| t.locale.to_sym }
|
78
|
+
end
|
76
79
|
end
|
77
80
|
|
78
81
|
def interpolates?(key)
|
@@ -80,13 +83,27 @@ module I18n
|
|
80
83
|
end
|
81
84
|
|
82
85
|
def value
|
86
|
+
value = read_attribute(:value)
|
83
87
|
if is_proc
|
84
|
-
Kernel.eval(
|
88
|
+
Kernel.eval(value)
|
89
|
+
elsif value == FALSY_CHAR
|
90
|
+
false
|
91
|
+
elsif value == TRUTHY_CHAR
|
92
|
+
true
|
85
93
|
else
|
86
|
-
value
|
87
|
-
value == 'f' ? false : value
|
94
|
+
value
|
88
95
|
end
|
89
96
|
end
|
97
|
+
|
98
|
+
def value=(value)
|
99
|
+
if value === false
|
100
|
+
value = FALSY_CHAR
|
101
|
+
elsif value === true
|
102
|
+
value = TRUTHY_CHAR
|
103
|
+
end
|
104
|
+
|
105
|
+
write_attribute(:value, value)
|
106
|
+
end
|
90
107
|
end
|
91
108
|
end
|
92
109
|
end
|
data/lib/i18n/backend/base.rb
CHANGED
@@ -8,10 +8,9 @@ module I18n
|
|
8
8
|
module Base
|
9
9
|
include I18n::Backend::Transliterator
|
10
10
|
|
11
|
-
RESERVED_KEYS = [:scope, :default, :separator, :resolve]
|
11
|
+
RESERVED_KEYS = [:scope, :default, :separator, :resolve, :object, :fallback]
|
12
12
|
RESERVED_KEYS_PATTERN = /%\{(#{RESERVED_KEYS.join("|")})\}/
|
13
13
|
DEPRECATED_INTERPOLATION_SYNTAX_PATTERN = /(\\)?\{\{([^\}]+)\}\}/
|
14
|
-
INTERPOLATION_SYNTAX_PATTERN = /%\{([^\}]+)\}/
|
15
14
|
|
16
15
|
# Accepts a list of paths to translation files. Loads translations from
|
17
16
|
# plain Ruby (*.rb) or YAML files (*.yml). See #load_rb and #load_yml
|
@@ -29,8 +28,6 @@ module I18n
|
|
29
28
|
|
30
29
|
def translate(locale, key, options = {})
|
31
30
|
raise InvalidLocale.new(locale) unless locale
|
32
|
-
return key.map { |k| translate(locale, k, options) } if key.is_a?(Array)
|
33
|
-
|
34
31
|
entry = key && lookup(locale, key, options[:scope], options)
|
35
32
|
|
36
33
|
if options.empty?
|
@@ -57,9 +54,10 @@ module I18n
|
|
57
54
|
raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime)
|
58
55
|
|
59
56
|
if Symbol === format
|
60
|
-
key
|
57
|
+
key = format
|
61
58
|
type = object.respond_to?(:sec) ? 'time' : 'date'
|
62
|
-
|
59
|
+
options = options.merge(:raise => true, :object => object, :locale => locale)
|
60
|
+
format = I18n.t(:"#{type}.formats.#{key}", options)
|
63
61
|
end
|
64
62
|
|
65
63
|
# format = resolve(locale, object, format, options)
|
@@ -113,14 +111,14 @@ module I18n
|
|
113
111
|
# If the given subject is a Symbol, it will be translated with the
|
114
112
|
# given options. If it is a Proc then it will be evaluated. All other
|
115
113
|
# subjects will be returned directly.
|
116
|
-
def resolve(locale, object, subject, options =
|
114
|
+
def resolve(locale, object, subject, options = {})
|
117
115
|
return subject if options[:resolve] == false
|
118
116
|
case subject
|
119
117
|
when Symbol
|
120
|
-
I18n.translate(subject,
|
118
|
+
I18n.translate(subject, options.merge(:locale => locale, :raise => true))
|
121
119
|
when Proc
|
122
120
|
date_or_time = options.delete(:object) || object
|
123
|
-
resolve(locale, object, subject.call(date_or_time, options)
|
121
|
+
resolve(locale, object, subject.call(date_or_time, options))
|
124
122
|
else
|
125
123
|
subject
|
126
124
|
end
|
@@ -151,50 +149,29 @@ module I18n
|
|
151
149
|
# interpolation).
|
152
150
|
def interpolate(locale, string, values = {})
|
153
151
|
return string unless string.is_a?(::String) && !values.empty?
|
154
|
-
original_values = values.dup
|
155
|
-
|
156
|
-
preserve_encoding(string) do
|
157
|
-
string = string.gsub(DEPRECATED_INTERPOLATION_SYNTAX_PATTERN) do
|
158
|
-
escaped, key = $1, $2.to_sym
|
159
|
-
if escaped
|
160
|
-
"{{#{key}}}"
|
161
|
-
else
|
162
|
-
warn_syntax_deprecation!
|
163
|
-
"%{#{key}}"
|
164
|
-
end
|
165
|
-
end
|
166
152
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
values[key] = value
|
175
|
-
else
|
176
|
-
values.delete(key)
|
177
|
-
end
|
153
|
+
string = string.gsub(DEPRECATED_INTERPOLATION_SYNTAX_PATTERN) do
|
154
|
+
escaped, key = $1, $2.to_sym
|
155
|
+
if escaped
|
156
|
+
"{{#{key}}}"
|
157
|
+
else
|
158
|
+
warn_syntax_deprecation!(locale, string)
|
159
|
+
"%{#{key}}"
|
178
160
|
end
|
161
|
+
end
|
179
162
|
|
180
|
-
|
163
|
+
values.each do |key, value|
|
164
|
+
value = value.call(values) if interpolate_lambda?(value, string, key)
|
165
|
+
value = value.to_s unless value.is_a?(::String)
|
166
|
+
values[key] = value
|
181
167
|
end
|
168
|
+
|
169
|
+
string % values
|
182
170
|
rescue KeyError => e
|
183
171
|
if string =~ RESERVED_KEYS_PATTERN
|
184
172
|
raise ReservedInterpolationKey.new($1.to_sym, string)
|
185
173
|
else
|
186
|
-
raise MissingInterpolationArgument.new(
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
def preserve_encoding(string)
|
191
|
-
if string.respond_to?(:encoding)
|
192
|
-
encoding = string.encoding
|
193
|
-
result = yield
|
194
|
-
result.force_encoding(encoding) if result.respond_to?(:force_encoding)
|
195
|
-
result
|
196
|
-
else
|
197
|
-
yield
|
174
|
+
raise MissingInterpolationArgument.new(values, string)
|
198
175
|
end
|
199
176
|
end
|
200
177
|
|
@@ -210,9 +187,10 @@ module I18n
|
|
210
187
|
# for all other file extensions.
|
211
188
|
def load_file(filename)
|
212
189
|
type = File.extname(filename).tr('.', '').downcase
|
213
|
-
raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}")
|
214
|
-
data = send(:"load_#{type}", filename)
|
215
|
-
|
190
|
+
raise UnknownFileType.new(type, filename) unless respond_to?(:"load_#{type}", true)
|
191
|
+
data = send(:"load_#{type}", filename)
|
192
|
+
raise InvalidLocaleData.new(filename) unless data.is_a?(Hash)
|
193
|
+
data.each { |locale, d| store_translations(locale, d || {}) }
|
216
194
|
end
|
217
195
|
|
218
196
|
# Loads a plain Ruby translations file. eval'ing the file must yield
|
@@ -224,12 +202,12 @@ module I18n
|
|
224
202
|
# Loads a YAML translations file. The data must have locales as
|
225
203
|
# toplevel keys.
|
226
204
|
def load_yml(filename)
|
227
|
-
YAML
|
205
|
+
YAML.load_file(filename)
|
228
206
|
end
|
229
207
|
|
230
|
-
def warn_syntax_deprecation! #:nodoc:
|
208
|
+
def warn_syntax_deprecation!(locale, string) #:nodoc:
|
231
209
|
return if @skip_syntax_deprecation
|
232
|
-
warn "The {{key}} interpolation syntax in I18n messages is deprecated. Please use %{key} instead.\n#{
|
210
|
+
warn "The {{key}} interpolation syntax in I18n messages is deprecated. Please use %{key} instead.\n#{locale} - #{string}\n"
|
233
211
|
@skip_syntax_deprecation = true
|
234
212
|
end
|
235
213
|
end
|
data/lib/i18n/backend/cache.rb
CHANGED
@@ -6,11 +6,11 @@
|
|
6
6
|
# To enable caching you can simply include the Cache module to the Simple
|
7
7
|
# backend - or whatever other backend you are using:
|
8
8
|
#
|
9
|
-
#
|
9
|
+
# I18n::Backend::Simple.send(:include, I18n::Backend::Cache)
|
10
10
|
#
|
11
11
|
# You will also need to set a cache store implementation that you want to use:
|
12
12
|
#
|
13
|
-
#
|
13
|
+
# I18n.cache_store = ActiveSupport::Cache.lookup_store(:memory_store)
|
14
14
|
#
|
15
15
|
# You can use any cache implementation you want that provides the same API as
|
16
16
|
# ActiveSupport::Cache (only the methods #fetch and #write are being used).
|
@@ -18,6 +18,23 @@
|
|
18
18
|
# The cache_key implementation assumes that you only pass values to
|
19
19
|
# I18n.translate that return a valid key from #hash (see
|
20
20
|
# http://www.ruby-doc.org/core/classes/Object.html#M000337).
|
21
|
+
#
|
22
|
+
# If you use a lambda as a default value in your translation like this:
|
23
|
+
#
|
24
|
+
# I18n.t(:"date.order", :default => lambda {[:month, :day, :year]})
|
25
|
+
#
|
26
|
+
# Then you will always have a cache miss, because each time this method
|
27
|
+
# is called the lambda will have a different hash value. If you know
|
28
|
+
# the result of the lambda is a constant as in the example above, then
|
29
|
+
# to cache this you can make the lambda a constant, like this:
|
30
|
+
#
|
31
|
+
# DEFAULT_DATE_ORDER = lambda {[:month, :day, :year]}
|
32
|
+
# ...
|
33
|
+
# I18n.t(:"date.order", :default => DEFAULT_DATE_ORDER)
|
34
|
+
#
|
35
|
+
# If the lambda may result in different values for each call then consider
|
36
|
+
# also using the Memoize backend.
|
37
|
+
#
|
21
38
|
module I18n
|
22
39
|
class << self
|
23
40
|
@@cache_store = nil
|
@@ -47,31 +64,41 @@ module I18n
|
|
47
64
|
module Backend
|
48
65
|
# TODO Should the cache be cleared if new translations are stored?
|
49
66
|
module Cache
|
50
|
-
def translate(
|
51
|
-
I18n.perform_caching? ? fetch(
|
67
|
+
def translate(locale, key, options = {})
|
68
|
+
I18n.perform_caching? ? fetch(cache_key(locale, key, options)) { super } : super
|
52
69
|
end
|
53
70
|
|
54
71
|
protected
|
55
72
|
|
56
|
-
def fetch(
|
57
|
-
result =
|
73
|
+
def fetch(cache_key, &block)
|
74
|
+
result = fetch_storing_missing_translation_exception(cache_key, &block)
|
58
75
|
raise result if result.is_a?(Exception)
|
59
76
|
result = result.dup if result.frozen? rescue result
|
60
77
|
result
|
78
|
+
end
|
79
|
+
|
80
|
+
def fetch_storing_missing_translation_exception(cache_key, &block)
|
81
|
+
fetch_ignoring_procs(cache_key, &block)
|
61
82
|
rescue MissingTranslationData => exception
|
62
|
-
I18n.cache_store.write(cache_key
|
63
|
-
|
83
|
+
I18n.cache_store.write(cache_key, exception)
|
84
|
+
exception
|
64
85
|
end
|
65
86
|
|
66
|
-
def cache_key
|
87
|
+
def fetch_ignoring_procs(cache_key, &block)
|
88
|
+
I18n.cache_store.read(cache_key) || yield.tap do |result|
|
89
|
+
I18n.cache_store.write(cache_key, result) unless result.is_a?(Proc)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def cache_key(locale, key, options)
|
67
94
|
# This assumes that only simple, native Ruby values are passed to I18n.translate.
|
68
|
-
|
69
|
-
# (see http://paulbarry.com/articles/2009/09/14/why-rails-3-will-require-ruby-1-8-7)
|
70
|
-
# If args.inspect does not work for you for some reason, patches are very welcome :)
|
71
|
-
hash = RUBY_VERSION >= "1.8.7" ? args.hash : args.inspect
|
72
|
-
keys = ['i18n', I18n.cache_namespace, hash]
|
73
|
-
keys.compact.join('-')
|
95
|
+
"i18n/#{I18n.cache_namespace}/#{locale}/#{key.hash}/#{USE_INSPECT_HASH ? options.inspect.hash : options.hash}"
|
74
96
|
end
|
97
|
+
|
98
|
+
private
|
99
|
+
# In Ruby < 1.9 the following is true: { :foo => 1, :bar => 2 }.hash == { :foo => 2, :bar => 1 }.hash
|
100
|
+
# Therefore we must use the hash of the inspect string instead to avoid cache key colisions.
|
101
|
+
USE_INSPECT_HASH = RUBY_VERSION <= "1.9"
|
75
102
|
end
|
76
103
|
end
|
77
|
-
end
|
104
|
+
end
|
data/lib/i18n/backend/chain.rb
CHANGED
@@ -17,61 +17,66 @@ 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
|
-
|
20
|
+
module Implementation
|
21
|
+
include Base
|
21
22
|
|
22
|
-
|
23
|
+
attr_accessor :backends
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
25
|
+
def initialize(*backends)
|
26
|
+
self.backends = backends
|
27
|
+
end
|
27
28
|
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
def reload!
|
30
|
+
backends.each { |backend| backend.reload! }
|
31
|
+
end
|
31
32
|
|
32
|
-
|
33
|
-
|
34
|
-
|
33
|
+
def store_translations(locale, data, options = {})
|
34
|
+
backends.first.store_translations(locale, data, options = {})
|
35
|
+
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
def available_locales
|
38
|
+
backends.map { |backend| backend.available_locales }.flatten.uniq
|
39
|
+
end
|
39
40
|
|
40
|
-
|
41
|
-
|
41
|
+
def translate(locale, key, default_options = {})
|
42
|
+
namespace = nil
|
43
|
+
options = default_options.except(:default)
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
45
|
+
backends.each do |backend|
|
46
|
+
begin
|
47
|
+
options = default_options if backend == backends.last
|
48
|
+
translation = backend.translate(locale, key, options)
|
49
|
+
if namespace_lookup?(translation, options)
|
50
|
+
namespace ||= {}
|
51
|
+
namespace.merge!(translation)
|
52
|
+
elsif !translation.nil?
|
53
|
+
return translation
|
54
|
+
end
|
55
|
+
rescue MissingTranslationData
|
53
56
|
end
|
54
|
-
rescue MissingTranslationData
|
55
57
|
end
|
58
|
+
|
59
|
+
return namespace if namespace
|
60
|
+
raise(I18n::MissingTranslationData.new(locale, key, options))
|
56
61
|
end
|
57
|
-
return namespace unless namespace.empty?
|
58
|
-
raise(I18n::MissingTranslationData.new(locale, key, options))
|
59
|
-
end
|
60
62
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
def localize(locale, object, format = :default, options = {})
|
64
|
+
backends.each do |backend|
|
65
|
+
begin
|
66
|
+
result = backend.localize(locale, object, format, options) and return result
|
67
|
+
rescue MissingTranslationData
|
68
|
+
end
|
66
69
|
end
|
70
|
+
raise(I18n::MissingTranslationData.new(locale, format, options))
|
67
71
|
end
|
68
|
-
|
72
|
+
|
73
|
+
protected
|
74
|
+
def namespace_lookup?(result, options)
|
75
|
+
result.is_a?(Hash) && !options.has_key?(:count)
|
76
|
+
end
|
69
77
|
end
|
70
78
|
|
71
|
-
|
72
|
-
def namespace_lookup?(result, options)
|
73
|
-
result.is_a?(Hash) and not options.has_key?(:count)
|
74
|
-
end
|
79
|
+
include Implementation
|
75
80
|
end
|
76
81
|
end
|
77
82
|
end
|
@@ -35,11 +35,13 @@ module I18n
|
|
35
35
|
# usual.
|
36
36
|
#
|
37
37
|
# The default option takes precedence over fallback locales
|
38
|
-
# only when it's
|
39
|
-
# is evaluated after fallback locales.
|
38
|
+
# only when it's a Symbol. When the default contains a String or a Proc
|
39
|
+
# it is evaluated last after all the fallback locales have been tried.
|
40
40
|
def translate(locale, key, options = {})
|
41
|
-
|
41
|
+
return super if options[:fallback]
|
42
|
+
default = extract_string_or_lambda_default!(options) if options[:default]
|
42
43
|
|
44
|
+
options[:fallback] = true
|
43
45
|
I18n.fallbacks[locale].each do |fallback|
|
44
46
|
begin
|
45
47
|
result = super(fallback, key, options)
|
@@ -47,21 +49,22 @@ module I18n
|
|
47
49
|
rescue I18n::MissingTranslationData
|
48
50
|
end
|
49
51
|
end
|
52
|
+
options.delete(:fallback)
|
50
53
|
|
51
54
|
return super(locale, nil, options.merge(:default => default)) if default
|
52
55
|
raise(I18n::MissingTranslationData.new(locale, key, options))
|
53
56
|
end
|
54
57
|
|
55
|
-
def
|
58
|
+
def extract_string_or_lambda_default!(options)
|
56
59
|
defaults = Array(options[:default])
|
57
|
-
if index =
|
60
|
+
if index = find_first_string_or_lambda_default(defaults)
|
58
61
|
options[:default] = defaults[0, index]
|
59
62
|
defaults[index]
|
60
63
|
end
|
61
64
|
end
|
62
65
|
|
63
|
-
def
|
64
|
-
defaults.
|
66
|
+
def find_first_string_or_lambda_default(defaults)
|
67
|
+
defaults.each_with_index { |default, ix| return ix if String === default || Proc === default }
|
65
68
|
nil
|
66
69
|
end
|
67
70
|
end
|
data/lib/i18n/backend/gettext.rb
CHANGED
@@ -49,9 +49,7 @@ module I18n
|
|
49
49
|
normalized = { part => normalized.empty? ? value : normalized }
|
50
50
|
end
|
51
51
|
|
52
|
-
|
53
|
-
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
|
54
|
-
result.merge!(normalized, &merger)
|
52
|
+
result.deep_merge!(normalized)
|
55
53
|
end
|
56
54
|
result
|
57
55
|
end
|
data/lib/i18n/backend/simple.rb
CHANGED
@@ -71,11 +71,11 @@ module I18n
|
|
71
71
|
init_translations unless initialized?
|
72
72
|
keys = I18n.normalize_keys(locale, key, scope, options[:separator])
|
73
73
|
|
74
|
-
keys.inject(translations) do |result,
|
75
|
-
|
76
|
-
return nil unless result.is_a?(Hash) && result.has_key?(
|
77
|
-
result = result[
|
78
|
-
result = resolve(locale,
|
74
|
+
keys.inject(translations) do |result, _key|
|
75
|
+
_key = _key.to_sym
|
76
|
+
return nil unless result.is_a?(Hash) && result.has_key?(_key)
|
77
|
+
result = result[_key]
|
78
|
+
result = resolve(locale, _key, result, options.merge(:scope => nil)) if result.is_a?(Symbol)
|
79
79
|
result
|
80
80
|
end
|
81
81
|
end
|
data/lib/i18n/config.rb
CHANGED
@@ -32,15 +32,17 @@ module I18n
|
|
32
32
|
end
|
33
33
|
|
34
34
|
# Returns an array of locales for which translations are available.
|
35
|
-
# Unless you explicitely set
|
36
|
-
# the call will be delegated to the backend
|
35
|
+
# Unless you explicitely set these through I18n.available_locales=
|
36
|
+
# the call will be delegated to the backend.
|
37
37
|
def available_locales
|
38
|
-
@@available_locales ||=
|
38
|
+
@@available_locales ||= nil
|
39
|
+
@@available_locales || backend.available_locales
|
39
40
|
end
|
40
41
|
|
41
42
|
# Sets the available locales.
|
42
43
|
def available_locales=(locales)
|
43
|
-
@@available_locales = locales
|
44
|
+
@@available_locales = Array(locales).map {|locale| locale.to_sym}
|
45
|
+
@@available_locales = nil if @@available_locales.empty?
|
44
46
|
end
|
45
47
|
|
46
48
|
# Returns the current default scope separator. Defaults to '.'
|
data/lib/i18n/exceptions.rb
CHANGED
@@ -17,10 +17,20 @@ module I18n
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
class InvalidLocaleData < ArgumentError
|
21
|
+
attr_reader :filename
|
22
|
+
def initialize(filename)
|
23
|
+
@filename = filename
|
24
|
+
super "can not load translations from #{filename}, expected it to return a hash, but does not"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
20
28
|
class MissingTranslationData < ArgumentError
|
21
29
|
attr_reader :locale, :key, :options
|
22
30
|
def initialize(locale, key, opts = nil)
|
23
|
-
@key, @locale, @options = key, locale, opts || {}
|
31
|
+
@key, @locale, @options = key, locale, opts.dup || {}
|
32
|
+
options.each { |k, v| options[k] = v.inspect if v.is_a?(Proc) }
|
33
|
+
|
24
34
|
keys = I18n.normalize_keys(locale, key, options[:scope])
|
25
35
|
keys << 'no key' if keys.size < 2
|
26
36
|
super "translation missing: #{keys.join(', ')}"
|
data/lib/i18n/gettext/helpers.rb
CHANGED
@@ -6,7 +6,7 @@ module I18n
|
|
6
6
|
# Implements classical Gettext style accessors. To use this include the
|
7
7
|
# module to the global namespace or wherever you want to use it.
|
8
8
|
#
|
9
|
-
# include I18n::Helpers
|
9
|
+
# include I18n::Gettext::Helpers
|
10
10
|
module Helpers
|
11
11
|
def gettext(msgid, options = {})
|
12
12
|
I18n.t(msgid, { :default => msgid, :separator => '|' }.merge(options))
|
data/lib/i18n/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: i18n
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 4
|
9
|
-
-
|
10
|
-
version: 0.4.
|
9
|
+
- 2
|
10
|
+
version: 0.4.2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Sven Fuchs
|
@@ -19,7 +19,7 @@ autorequire:
|
|
19
19
|
bindir: bin
|
20
20
|
cert_chain: []
|
21
21
|
|
22
|
-
date: 2010-
|
22
|
+
date: 2010-10-26 00:00:00 +02:00
|
23
23
|
default_executable:
|
24
24
|
dependencies: []
|
25
25
|
|
@@ -93,12 +93,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
hash:
|
96
|
+
hash: 17
|
97
97
|
segments:
|
98
98
|
- 1
|
99
99
|
- 3
|
100
|
-
-
|
101
|
-
version: 1.3.
|
100
|
+
- 5
|
101
|
+
version: 1.3.5
|
102
102
|
requirements: []
|
103
103
|
|
104
104
|
rubyforge_project: "[none]"
|