i18n-inflector 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -42,7 +42,7 @@ module I18n
42
42
  # Translates given key taking care of inflections.
43
43
  #
44
44
  # @api public
45
- # @see I18n::Inflector::Core#interpolate
45
+ # @see I18n::Inflector::API#interpolate
46
46
  # @see I18n::Inflector::InflectionOptions
47
47
  # @param [Symbol] locale locale
48
48
  # @param [Symbol,String] key translation key
@@ -52,12 +52,13 @@ module I18n
52
52
  # particular option should be the same as the name of a kind of tokens from a pattern.
53
53
  # All +options+ along with a +string+ and +locale+ are passed to
54
54
  # {I18n::Backend::Simple#translate I18n::Backend::Simple#translate}
55
- # and the result is processed by {I18n::Inflector::Core#interpolate}
55
+ # and the result is processed by {I18n::Inflector::API#interpolate}
56
56
  # @return [String] the translated string with interpolated patterns
57
57
  def translate(locale, key, options = {})
58
+ orig_options = options.dup
58
59
  translated_string = super
59
60
 
60
- return translated_string if locale.to_s.empty?
61
+ return translated_string if (locale.nil? || locale.to_s.empty?)
61
62
 
62
63
  unless @inflector.inflected_locale?(locale)
63
64
  return translated_string.gsub(I18n::Inflector::PATTERN,'')
@@ -67,7 +68,7 @@ module I18n
67
68
  return translated_string
68
69
  end
69
70
 
70
- @inflector.interpolate(translated_string, locale, options.dup)
71
+ @inflector.interpolate(translated_string, locale, orig_options)
71
72
  end
72
73
 
73
74
  # Stores translations in memory.
@@ -88,8 +89,8 @@ module I18n
88
89
  unless subdata.nil?
89
90
  subdata = (subdata[:inflections] || subdata['inflections'])
90
91
  unless subdata.nil?
91
- inflection_data = load_inflection_tokens(locale, r[:i18n][:inflections])
92
- @inflector.add_database(inflection_data)
92
+ db, db_strict = load_inflection_tokens(locale, r[:i18n][:inflections])
93
+ @inflector.add_databases(db, db_strict)
93
94
  end
94
95
  end
95
96
  end
@@ -103,7 +104,7 @@ module I18n
103
104
  # @return [void]
104
105
  def inflector_try_init
105
106
  return nil if (defined?(@inflector) && !@inflector.nil?)
106
- @inflector = I18n::Inflector::Core.new
107
+ @inflector = I18n::Inflector::API.new
107
108
  nil
108
109
  end
109
110
 
@@ -122,7 +123,7 @@ module I18n
122
123
  end
123
124
 
124
125
  # Gives an access to the internal structure containing configuration data
125
- # for a given locale.
126
+ # for the given locale.
126
127
  #
127
128
  # @note Under some very rare conditions this method may be called while
128
129
  # translation data is loading. It must always return when translations
@@ -130,7 +131,7 @@ module I18n
130
131
  # will eat a kittien!
131
132
  # @param [Symbol] locale the locale to use
132
133
  # @return [Hash,nil] part of the translation data that
133
- # reflects inflections for a given locale or +nil+
134
+ # reflects inflections for the given locale or +nil+
134
135
  # if translations are not initialized
135
136
  def inflection_subtree(locale)
136
137
  return nil unless initialized?
@@ -174,13 +175,13 @@ module I18n
174
175
  if kind_subtree.has_key?(token)
175
176
  return token
176
177
  else
177
- # that should never happend but who knows
178
178
  raise I18n::BadInflectionToken.new(locale, token, kind)
179
179
  end
180
180
  else
181
181
  orig_token = token
182
182
  token = value[1..-1]
183
- if token.to_s.empty?
183
+
184
+ if (token.nil? || token.to_s.empty?)
184
185
  raise I18n::BadInflectionToken.new(locale, token, kind)
185
186
  end
186
187
  token = token.to_sym
@@ -195,7 +196,8 @@ module I18n
195
196
 
196
197
  # Uses the inflections subtree and creates internal mappings
197
198
  # to resolve kinds assigned to inflection tokens and aliases, including defaults.
198
- # @return [Hash,nil] the internal Hash containing inflections tokens or +nil+ if something went wrong
199
+ # @return [I18n::Inflector::InflectionData,nil] the database containing inflections tokens
200
+ # or +nil+ if something went wrong
199
201
  # @raise [I18n::BadInflectionToken] if a name of some loaded token is invalid
200
202
  # @raise [I18n::BadInflectionAlias] if a loaded alias points to a token that does not exists
201
203
  # @raise [I18n::DuplicatedInflectionToken] if a token has already appeard in loaded configuration
@@ -204,65 +206,100 @@ module I18n
204
206
  # Loads inflection tokens for the given locale using internal hash of stored translations. Requires
205
207
  # translations to be initialized.
206
208
  # @param [Symbol] locale the locale to use and work for
207
- # @return [Hash,nil] the internal Hash containing inflections or +nil+ if translations were not initialized
209
+ # @return [I18n::Inflector::InflectionData,nil] the database containing inflections tokens
210
+ # or +nil+ if something went wrong
208
211
  # @overload load_inflection_tokens(locale, subtree)
209
- # Loads inflection tokens for the given locale using data given in an argument
212
+ # Loads inflection tokens for the given locale using datthe given in an argument
210
213
  # @param [Symbol] locale the locale to use and work for
211
214
  # @param [Hash] subtree the tree (in a form of nested Hashes) containing inflection tokens to scan
212
- # @return [Hash,nil] the internal Hash containing inflections or +nil+ if the given subtree was wrong or empty
215
+ # @return [I18n::Inflector::InflectionData,nil] the database containing inflections tokens
216
+ # or +nil+ if something went wrong
213
217
  def load_inflection_tokens(locale, subtree=nil)
214
218
  inflections_tree = subtree || inflection_subtree(locale)
215
219
  return nil if (inflections_tree.nil? || inflections_tree.empty?)
216
220
 
217
- idb = I18n::Inflector::InflectionData.new(locale)
221
+ idb = I18n::Inflector::InflectionData.new(locale)
222
+ idb_strict = I18n::Inflector::InflectionData_Strict.new(locale)
223
+
224
+ return nil if (idb.nil? || idb_strict.nil?)
218
225
 
219
- inflections_tree.each_pair do |kind, tokens|
226
+ inflections = prepare_inflections(inflections_tree, idb, idb_strict)
227
+
228
+ inflections.each do |orig_kind, kind, strict_kind, subdb, tokens|
220
229
  tokens.each_pair do |token, description|
221
230
 
222
231
  # test for duplicate
223
- if idb.has_token?(token)
224
- raise I18n::DuplicatedInflectionToken.new(idb.get_kind(token), kind, token)
232
+ if subdb.has_token?(token, strict_kind)
233
+ raise I18n::DuplicatedInflectionToken.new(subdb.get_kind(token, strict_kind), orig_kind, token)
225
234
  end
226
235
 
227
236
  # validate token's name
228
- raise I18n::BadInflectionToken.new(locale, token, kind) if token.to_s.empty?
237
+ raise I18n::BadInflectionToken.new(locale, token, orig_kind) if (token.nil? || token.to_s.empty?)
229
238
 
230
239
  # validate token's description
231
240
  if description.nil?
232
- raise I18n::BadInflectionToken.new(locale, token, kind, description)
241
+ raise I18n::BadInflectionToken.new(locale, token, orig_kind, description)
233
242
  elsif description[0..0] == I18n::Inflector::ALIAS_MARKER
234
243
  next
235
244
  end
236
245
 
237
- # handle default token for a kind
238
- if token == :default
239
- if idb.has_default_token?(kind) # should never happend unless someone is messing with @translations
240
- raise I18n::DuplicatedInflectionToken.new(kind, nil, token)
241
- end
242
- idb.set_default_token(kind, description)
243
- next
244
- end
246
+ # skip default token for later processing
247
+ next if token == :default
245
248
 
246
- idb.add_token(token, kind, description)
249
+ subdb.add_token(token, kind, description)
247
250
  end
248
251
  end
249
252
 
250
253
  # handle aliases
251
- inflections_tree.each_pair do |kind, tokens|
254
+ inflections.each do |orig_kind, kind, strict_kind, subdb, tokens|
252
255
  tokens.each_pair do |token, description|
256
+ next if token == :default
253
257
  next if description[0..0] != I18n::Inflector::ALIAS_MARKER
254
- real_token = shorten_inflection_alias(token, kind, locale, inflections_tree)
255
- idb.add_alias(token, real_token) unless real_token.nil?
258
+ real_token = shorten_inflection_alias(token, orig_kind, locale, inflections_tree)
259
+ subdb.add_alias(token, real_token, kind) unless real_token.nil?
256
260
  end
257
261
  end
258
262
 
259
- # process and validate defaults
260
- valid = idb.validate_default_tokens
261
- raise I18n::BadInflectionAlias.new(locale, :default, valid[0], valid[1]) unless valid.nil?
263
+ # handle default tokens
264
+ inflections.each do |orig_kind, kind, strict_kind, subdb, tokens|
265
+ next unless tokens.has_key?(:default)
266
+ if subdb.has_default_token?(kind)
267
+ raise I18n::DuplicatedInflectionToken.new(orig_kind, nil, :default)
268
+ end
269
+ orig_target = tokens[:default]
270
+ target = orig_target.to_s
271
+ target = target[1..-1] if target[0..0] == I18n::Inflector::ALIAS_MARKER
272
+ if target.empty?
273
+ raise I18n::BadInflectionToken.new(locale, token, orig_kind, orig_target)
274
+ end
275
+ target = subdb.get_true_token(target.to_sym, kind)
276
+ if target.nil?
277
+ raise I18n::BadInflectionAlias.new(locale, :default, orig_kind, orig_target)
278
+ end
279
+ subdb.set_default_token(kind, target)
280
+ end
281
+
282
+ [idb, idb_strict]
283
+ end
262
284
 
263
- idb
285
+ # @private
286
+ def prepare_inflections(inflections, idb, idb_strict)
287
+ I18n::Inflector::LazyHashEnumerator.new(inflections).ary_map do |kind, tokens|
288
+ next if (tokens.nil? || tokens.empty?)
289
+ subdb = idb
290
+ strict_kind = nil
291
+ orig_kind = kind
292
+ if kind.to_s[0..0] == I18n::Inflector::NAMED_MARKER
293
+ kind = kind.to_s[1..-1]
294
+ next if kind.empty?
295
+ kind = kind.to_sym
296
+ subdb = idb_strict
297
+ strict_kind = kind
298
+ end
299
+ [orig_kind, kind, strict_kind, subdb, tokens]
300
+ end
264
301
  end
265
302
 
266
- end
267
- end
268
- end
303
+ end # module Inflector
304
+ end # module Backend
305
+ end # module I18n
@@ -8,78 +8,119 @@
8
8
 
9
9
  module I18n
10
10
 
11
- # This is raised when there is no kind given in options or the kind is +nil+. The kind
12
- # is determined by looking at token placed in a pattern.
13
- #
14
- # This exception will also be raised when a required option, describing token selected
15
- # for a kind, is empty or doesn't match any acceptable tokens.
11
+ # @abstract This class is a parent class for exceptions raised when
12
+ # inflection option is bad or missing.
16
13
  class InvalidOptionForKind < ArgumentError
14
+
17
15
  attr_reader :pattern, :kind, :token, :option
16
+
18
17
  def initialize(pattern, kind, token, option)
19
- @pattern, @kind, @token, @option = pattern, kind, token, option
20
- if option.nil?
21
- super "option #{kind.inspect} required by the " +
22
- "pattern #{pattern.inspect} was not found"
23
- else
24
- super "value #{option.inspect} of #{kind.inspect} required by the " +
25
- "pattern #{pattern.inspect} does not match any token"
18
+ @pattern, @kind, @token, @option, @option_present = pattern, kind, token, option
19
+ @message ||= ""
20
+ super(@message)
21
+ end
22
+
23
+ end
24
+
25
+ # This is raised when there is no kind given in options. The kind
26
+ # is determined by looking at token placed in a pattern.
27
+ class InflectionOptionNotFound < InvalidOptionForKind
28
+
29
+ def initialize(pattern, kind, token, option=nil)
30
+ kind = kind.to_s
31
+ unless kind.empty?
32
+ if kind[0..0] == I18n::Inflector::NAMED_MARKER
33
+ kind = ":#{kind} (or :#{kind[1..-1]})"
34
+ else
35
+ kind = kind.to_sym.inspect
36
+ end
26
37
  end
38
+ @message = "option #{kind} required by the " +
39
+ "pattern #{pattern.inspect} was not found"
40
+ super
41
+ end
42
+
43
+ end
44
+
45
+ # This exception will be raised when a required option, describing token selected
46
+ # for a kind, is +nil+, empty or doesn't match any acceptable tokens.
47
+ class InflectionOptionIncorrect < InvalidOptionForKind
48
+
49
+ def initialize(pattern, kind, token, option)
50
+ @message = "value #{option.inspect} of option #{kind.inspect} required by " +
51
+ "#{pattern.inspect} does not match any token"
52
+ super
27
53
  end
54
+
28
55
  end
29
56
 
30
57
  # This is raised when token given in pattern is invalid (empty or has no
31
58
  # kind assigned).
32
59
  class InvalidInflectionToken < ArgumentError
60
+
33
61
  attr_reader :pattern, :token
62
+
34
63
  def initialize(pattern, token)
35
64
  @pattern, @token = pattern, token
36
65
  super "token #{token.inspect} used in translation " +
37
66
  "pattern #{pattern.inspect} is invalid"
38
67
  end
68
+
39
69
  end
40
70
 
41
71
  # This is raised when an inflection token used in a pattern does not match
42
72
  # an assumed kind determined by reading previous tokens from that pattern.
43
73
  class MisplacedInflectionToken < ArgumentError
74
+
44
75
  attr_reader :pattern, :token, :kind
76
+
45
77
  def initialize(pattern, token, kind)
46
78
  @pattern, @token, @kind = pattern, token, kind
47
79
  super "inflection token #{token.inspect} from pattern #{pattern.inspect} " +
48
80
  "is not of the expected kind #{kind.inspect}"
49
81
  end
82
+
50
83
  end
51
84
 
52
85
  # This is raised when an inflection token of the same name is already defined in
53
86
  # inflections tree of translation data.
54
87
  class DuplicatedInflectionToken < ArgumentError
88
+
55
89
  attr_reader :original_kind, :kind, :token
90
+
56
91
  def initialize(original_kind, kind, token)
57
92
  @original_kind, @kind, @token = original_kind, kind, token
58
93
  and_cannot = kind.nil? ? "" : "and cannot be used with kind #{kind.inspect}"
59
94
  super "inflection token #{token.inspect} was already assigned " +
60
- "to kind #{original_kind}" + and_cannot
95
+ "to kind #{original_kind} " + and_cannot
61
96
  end
97
+
62
98
  end
63
99
 
64
100
  # This is raised when an alias for an inflection token points to a token that
65
101
  # doesn't exists. It is also raised when default token of some kind points
66
102
  # to a non-existent token.
67
103
  class BadInflectionAlias < ArgumentError
104
+
68
105
  attr_reader :locale, :token, :kind, :pointer
106
+
69
107
  def initialize(locale, token, kind, pointer)
70
108
  @locale, @token, @kind, @pointer = locale, token, kind, pointer
71
109
  what = token == :default ? "default token" : "alias"
72
110
  lang = locale.nil? ? "" : "for language #{locale.inspect} "
73
111
  kinn = kind.nil? ? "" : "of kind #{kind.inspect} "
74
- super "the #{what} #{token.inspect}" + kinn + lang +
112
+ super "the #{what} #{token.inspect} " + kinn + lang +
75
113
  "points to an unknown token #{pointer.inspect}"
76
114
  end
115
+
77
116
  end
78
117
 
79
118
  # This is raised when an inflection token or its description has a bad name. This
80
119
  # includes an empty name or a name containing prohibited characters.
81
120
  class BadInflectionToken < ArgumentError
121
+
82
122
  attr_reader :locale, :token, :kind, :description
123
+
83
124
  def initialize(locale, token, kind=nil, description=nil)
84
125
  @locale, @token, @kind, @description = locale, token, kind, description
85
126
  kinn = kind.nil? ? "" : "of kind #{kind.inspect} "
@@ -91,6 +132,7 @@ module I18n
91
132
  "for language #{locale.inspect} has a bad description #{description.inspect}"
92
133
  end
93
134
  end
135
+
94
136
  end
95
137
 
96
138
  end