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.
@@ -0,0 +1,120 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Author:: Paweł Wilk (mailto:pw@gnu.org)
4
+ # Copyright:: (c) 2011 by Paweł Wilk
5
+ # License:: This program is licensed under the terms of {file:LGPL GNU Lesser General Public License} or {file:COPYING Ruby License}.
6
+ #
7
+ # This file contains lazy enumerators.
8
+
9
+ module I18n
10
+ module Inflector
11
+
12
+ if RUBY_VERSION.gsub(/\D/,'')[0..1].to_i < 19
13
+ require 'enumerator' rescue nil
14
+
15
+ class LazyHashEnumerator < Object.const_defined?(:Enumerator) ? Enumerator : Enumerable::Enumerator
16
+
17
+ # This class allows to initialize the Enumerator with a block
18
+ class Yielder
19
+ def initialize(&block)
20
+ @main_block = block
21
+ end
22
+
23
+ def each(&block)
24
+ @final_block = block
25
+ @main_block.call(self)
26
+ end
27
+
28
+ if Proc.method_defined?(:yield)
29
+ def yield(*arg)
30
+ @final_block.yield(*arg)
31
+ end
32
+ else
33
+ def yield(*arg)
34
+ @final_block.call(*arg)
35
+ end
36
+ end
37
+ end
38
+
39
+ unless (self.new{} rescue false)
40
+ def initialize(*args, &block)
41
+ args.empty? ? super(Yielder.new(&block)) : super(*args, &nil)
42
+ end
43
+ end
44
+
45
+ if method_defined?(:with_object) and not method_defined?(:each_with_object)
46
+ alias_method :with_object, :each_with_object
47
+ end
48
+
49
+ end # class LazyHashEnumerator for ruby18
50
+
51
+ else # if RUBY_VERSION >= 1.9.0
52
+
53
+ class LazyHashEnumerator < Enumerator
54
+ end
55
+
56
+ end
57
+
58
+ # This class implements simple enumerators for hashes
59
+ # that allow to do lazy operations on them.
60
+ class LazyHashEnumerator
61
+
62
+ # Creates a Hash kind of object by collecting all
63
+ # data from enumerated collection.
64
+ # @return [Hash] the resulting hash
65
+ def to_h
66
+ Hash[self.to_a]
67
+ end
68
+
69
+ # Hash mapping enumerator
70
+ # @return [I18n::Inflector::LazyHashEnumerator] the enumerator
71
+ def map(&block)
72
+ LazyHashEnumerator.new do |yielder|
73
+ self.each do |k,v|
74
+ yielder.yield(k,block.call(k,v))
75
+ end
76
+ end
77
+ end
78
+
79
+ # Hash to Array mapping enumerator
80
+ # @return [I18n::Inflector::LazyEnumerator] the enumerator
81
+ def ary_map(&block)
82
+ LazyHashEnumerator.new do |yielder|
83
+ self.each do |value|
84
+ yielder.yield(block.call(value))
85
+ end
86
+ end
87
+ end
88
+
89
+ # This method converts resulting keys
90
+ # to array.
91
+ def keys
92
+ ary = []
93
+ self.each{ |k,v| ary << k }
94
+ return ary
95
+ end
96
+
97
+ # Hash selecting enumerator
98
+ # @return [I18n::Inflector::LazyHashEnumerator] the enumerator
99
+ def select(&block)
100
+ LazyHashEnumerator.new do |yielder|
101
+ self.each do |k,v|
102
+ yielder.yield(k,v) if block.call(k,v)
103
+ end
104
+ end
105
+ end
106
+
107
+ # Hash rejecting enumerator
108
+ # @return [I18n::Inflector::LazyHashEnumerator] the enumerator
109
+ def reject(&block)
110
+ LazyHashEnumerator.new do |yielder|
111
+ self.each do |k,v|
112
+ yielder.yield(k,v) unless block.call(k,v)
113
+ end
114
+ end
115
+ end
116
+
117
+ end # class LazyHashEnumerator
118
+
119
+ end
120
+ end
@@ -10,10 +10,11 @@
10
10
  #
11
11
 
12
12
  module I18n
13
- # @version 2.0
13
+ # @version 2.1
14
14
  # This module contains inflection classes and modules for enabling
15
15
  # the inflection support in I18n translations.
16
- # Its submodule overwrites the Simple backend translate method
16
+ # It is used by the module called {I18n::Backend::Inflector}
17
+ # that overwrites the Simple backend translate method
17
18
  # so that it will interpolate additional inflection tokens present
18
19
  # in translations. These tokens may appear in *patterns* which
19
20
  # are contained within <tt>@{</tt> and <tt>}</tt> symbols.
@@ -30,6 +31,9 @@ module I18n
30
31
  # i18n.inflector.true_tokens.keys
31
32
  # # => [:f, :m, :n]
32
33
  #
34
+ # See the {file:EXAMPLES} for more information about real-life
35
+ # usage of Inflector.
36
+ #
33
37
  # == Inflection pattern
34
38
  # An example inflection pattern contained in a translation record looks like:
35
39
  # welcome: "Dear @{f:Madam|m:Sir|n:You|All}"
@@ -39,9 +43,12 @@ module I18n
39
43
  # pattern. To select which one an additional option is used. That option
40
44
  # must be passed to translate method.
41
45
  #
46
+ # There are also so called <b>named patterns</b> that will be explained
47
+ # later.
48
+ #
42
49
  # == Configuration
43
- # To recognize tokens present in patterns this module uses keys grouped
44
- # in the scope called `inflections` for a given locale. For instance
50
+ # To recognize tokens present in patterns keys grouped
51
+ # in the scope called +inflections+ for the given locale are used. For instance
45
52
  # (YAML format):
46
53
  # en:
47
54
  # i18n:
@@ -76,19 +83,30 @@ module I18n
76
83
  # This is required in order to keep patterns simple and tokens interpolation
77
84
  # fast.
78
85
  #
79
- # Kind is also used to instruct I18n.translate method which
80
- # token it should pick. This will be explained later.
86
+ # Kind is also used to instruct {I18n::Backend::Inflector#translate} method which
87
+ # token it should pick. This is done through options and
88
+ # will be explained later.
89
+ #
90
+ # There is also a class of kind called <b>strict kind</b> used by
91
+ # named patterns; that will be explained later.
81
92
  #
82
93
  # === Tokens
83
- # The token is an element of a pattern. A pattern may have many tokens
94
+ # The token is an element of a pattern. Any pattern may have many tokens
84
95
  # of the same kind separated by vertical bars. Each token name used in a
85
96
  # pattern should end with colon sign. After this colon a value should
86
97
  # appear (or an empty string).
87
- #
98
+ #
99
+ # Tokens also appear in a configuration data. They are assigned to kinds.
100
+ # Token names must be unique across all kinds, since it would be impossible
101
+ # for interpolation routine to guess a kind of a token present in a pattern.
102
+ # There is however a class of kinds called strict kinds, for which tokens
103
+ # must be unique only within a kind. The named patterns that are using
104
+ # strict kinds will be explained later.
105
+ #
88
106
  # === Aliases
89
107
  # Aliases are special tokens that point to other tokens. They cannot
90
- # be used in inflection patterns but they are fully recognized values
91
- # of options while evaluating kinds.
108
+ # be used in inflection patterns but they are fully recognized options
109
+ # that can be passed to +translate+ method.
92
110
  #
93
111
  # Aliases might be helpful in multilingual applications that are using
94
112
  # a fixed set of values passed through options to describe some properties
@@ -150,6 +168,133 @@ module I18n
150
168
  # but might be helpful (e.g. in UI). For obvious reasons you cannot
151
169
  # describe aliases.
152
170
  #
171
+ # == Named patterns
172
+ #
173
+ # A named pattern is a pattern that may contain special clause
174
+ # containing name of a kind that tokens from a pattern
175
+ # are assigned to. It looks like:
176
+ #
177
+ # welcome: "Dear @gender{f:Madam|m:Sir|n:You|All}"
178
+ #
179
+ # === Configuring named patterns
180
+ #
181
+ # To recognize tokens present in a named patterns,
182
+ # inflector uses keys grouped in the scope called +inflections+
183
+ # for the given locale. For instance (YAML format):
184
+ # en:
185
+ # i18n:
186
+ # inflections:
187
+ # @gender:
188
+ # f: "female"
189
+ # woman: @f
190
+ # default: f
191
+ #
192
+ # Elements in the example above are:
193
+ # * +en+: language
194
+ # * +i18n+: configuration scope
195
+ # * +inflections+: inflections configuration scope
196
+ # * +gender+: <bb>strict kind</bb> scope
197
+ # * +f+: inflection token
198
+ # * <tt>"female"</tt>: token's description
199
+ # * +woman+: inflection alias
200
+ # * <tt>@f</tt>: pointer to real token
201
+ # * +default+: default token for a strict kind +gender+
202
+ #
203
+ # === Strict kinds
204
+ #
205
+ # In order to handle named patterns properly a new data structure
206
+ # is used. It is called the <b>strict kind</b>. Strict kinds are defined
207
+ # in a configuration in a similar way the regular kinds are but
208
+ # tokens assigned to them may have the same names across a whole
209
+ # configuration. (Note that within a strict kind tokens should still
210
+ # be unique.) That implies a requirement of passing the
211
+ # identifier of a kind when referring to such tokens.
212
+ #
213
+ # Here is the example configuration using strict kinds:
214
+ #
215
+ # en:
216
+ # i18n:
217
+ # inflections:
218
+ # @gender:
219
+ # f: "female"
220
+ # m: "male"
221
+ # n: "neuter"
222
+ # woman: @f
223
+ # man: @m
224
+ # default: n
225
+ # @title:
226
+ # s: "sir"
227
+ # l: "lady"
228
+ # u: "you"
229
+ # m: @s
230
+ # f: @l
231
+ # default: u
232
+ #
233
+ # The only thing that syntactically distinguishes named kinds
234
+ # from regular kinds is a presence of the +@+ symbol.
235
+ #
236
+ # You can mix regular and strict kinds having the same names.
237
+ # The proper class of kind will be picked up by interpolation
238
+ # method easily, since the first mentioned class uses
239
+ # patterns that are not named, and the second uses named patterns.
240
+ #
241
+ # ==== Strict kinds in options
242
+ #
243
+ # The interpolation routine recognizes strict kinds passed as
244
+ # options in almost the same way that it does it for regular
245
+ # kinds. The only difference is that you can override usage
246
+ # of a regular kind inflection option (if there is any) by
247
+ # putting a strict kind option with name prefixed by +@+ symbol.
248
+ # The inflection options starting with this symbol have
249
+ # precedence over inflection options without it;
250
+ # that is of course only true for strict kinds and has any effect
251
+ # only when both options describing the same kind are present.
252
+ #
253
+ # In other words: interpolation routine is looking for
254
+ # strict kinds in inflection options using their names
255
+ # with +@+ in front. When that fails it falls back to
256
+ # trying an option named like the strict kind but without
257
+ # the +@+ symbol. Examples:
258
+ #
259
+ # I18n.translate(welcome, :gender => :m, :@gender => :f)
260
+ # # the :f will be picked for the strict kind gender
261
+ #
262
+ # I18n.translate(welcome, :@gender => :f)
263
+ # # the :f will be picked for the strict kind gender
264
+ #
265
+ # I18n.translate(welcome, :gender => :f)
266
+ # # the :f will be picked for the strict kind gender
267
+ #
268
+ # In the example above we assume that +welcome+ is defined
269
+ # like that:
270
+ #
271
+ # welcome: "Dear @gender{f:Madam|m:Sir|n:You|All}"
272
+ #
273
+ # Note that for regular kinds the option named <tt>:@gender</tt>
274
+ # will have no meaning.
275
+ #
276
+ # ==== Note for developers
277
+ #
278
+ # Strict kinds that are used to handle named patterns
279
+ # are internally stored in a different database and handled by
280
+ # similar but different API methods than regular kinds. However
281
+ # most of the {I18n::Inflector::API} methods are also aware of strict kinds
282
+ # and will call proper methods oprating on strict inflections
283
+ # data when the +@+ symbol is detected at the beginning of
284
+ # the identifier of a kind passed as an argument. For example:
285
+ #
286
+ # I18n.inflector.has_token?(:m, :@gender)
287
+ #
288
+ # will effectively call:
289
+ #
290
+ # I18n.inflector.strict.has_token?(:m, :gender)
291
+ #
292
+ # As you can see above, to access {API_Strict} methods for strict kinds
293
+ # (and strict kinds data) only, associated with default I18n backend,
294
+ # use:
295
+ #
296
+ # I18n.inflector.strict
297
+ #
153
298
  # == Interpolation
154
299
  # The value of each token present in a pattern is to be picked by the interpolation
155
300
  # routine and will replace the whole pattern, when the token name from that
@@ -197,7 +342,7 @@ module I18n
197
342
  #
198
343
  # Be aware that enabling extended error reporting makes it unable
199
344
  # to use fallback values in most cases. Local fallbacks will then be
200
- # applied only when a given option contains a proper value for some
345
+ # applied only when the given option contains a proper value for some
201
346
  # kind but it's just not present in a pattern, for example:
202
347
  #
203
348
  # ===== YAML:
@@ -214,7 +359,7 @@ module I18n
214
359
  # I18n.translate('welcome', :gender => :o, :raises => true)
215
360
  # # => "Dear All"
216
361
  # # since the token :o was configured but not used in the pattern
217
- #
362
+ #
218
363
  # === Unknown and empty tokens in options
219
364
  # If an option containing token is not present at all then the interpolation
220
365
  # routine will try the default token for a processed kind if the default
@@ -287,9 +432,22 @@ module I18n
287
432
  # Normally it possible to use only true tokens in patterns, not aliases.
288
433
  # However, if you feel lucky and you're not affraid of messy patterns
289
434
  # you can use the switch {I18n::Inflector::InflectionOptions#aliased_patterns}
290
- # or corresponding +:inflector_aliased_patterns+ option passed to translation
435
+ # or corresponding <tt>:inflector_aliased_patterns</tt> option passed to translation
291
436
  # method.
292
437
  #
438
+ # It may seem very easy and attractive to use aliased patterns, especially
439
+ # in the environments where token comes from a user. In such cases aliases
440
+ # may be used as database that translates common words to inflection tokens
441
+ # that have meanings. For example user may enter a gender in some text
442
+ # field and it will be used as inflection token. To map different names
443
+ # (e.g. male, boy, sir, female, girl, lady) to exact inflection tokens
444
+ # the aliases would be used. Note hovewer, that you can make use of
445
+ # <tt>I18n.inflector.true_token</tt> method (see {I18n::Inflector::API#true_token}
446
+ # that will resolve any alias and then use that data to feed some inflection option
447
+ # (e.g. <tt>:gender</tt>). In such scenario you don't have to rely on aliases
448
+ # in patterns and you will gain some speed since resolving will occur just once,
449
+ # not each time translated text is interpolated.
450
+ #
293
451
  # === Escaping a pattern
294
452
  # If there is a need to translate something that matches an inflection
295
453
  # pattern the escape symbols can be used to disable the interpolation. These
@@ -335,9 +493,40 @@ module I18n
335
493
  # * {I18n::DuplicatedInflectionToken I18n::DuplicatedInflectionToken}
336
494
  # * {I18n::BadInflectionToken I18n::BadInflectionToken}
337
495
  # * {I18n::BadInflectionAlias I18n::BadInflectionAlias}
338
- #
339
496
  module Inflector
340
497
 
498
+ class API
499
+
500
+ # This reader allows to reach a reference of the
501
+ # object that is a kind of {I18n::Inflector::API_Strict}
502
+ # and handles inflections for named patterns (strict kinds).
503
+ #
504
+ # @api public
505
+ # @return [I18n::Inflector::API_Strict] the object containing
506
+ # database and operations for named patterns (strict kinds)
507
+ attr_reader :strict
508
+
509
+ # Gets known regular inflection kinds.
510
+ #
511
+ # @api public
512
+ # @note To get all inflection kinds (regular and strict) for default inflector
513
+ # use: <tt>I18n.inflector.kinds + I18n.inflector.strict.kinds</tt>
514
+ # @return [Array<Symbol>] the array containing known inflection kinds
515
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
516
+ # @overload kinds
517
+ # Gets known inflection kinds for the current locale.
518
+ # @return [Array<Symbol>] the array containing known inflection kinds
519
+ # @overload kinds(locale)
520
+ # Gets known inflection kinds for the given +locale+.
521
+ # @param [Symbol] locale the locale for which operation has to be done
522
+ # @return [Array<Symbol>] the array containing known inflection kinds
523
+ def kinds(locale=nil)
524
+ super
525
+ end
526
+ alias_method :inflection_kinds, :kinds
527
+
528
+ end
529
+
341
530
  end
342
531
 
343
532
  # @abstract This exception class is defined in package I18n. It is raised when
@@ -14,7 +14,7 @@ module I18n
14
14
  # @private
15
15
  EMAIL = 'pw@gnu.org'
16
16
  # @private
17
- VERSION = '2.0.1'
17
+ VERSION = '2.1.0'
18
18
  # @private
19
19
  NAME = 'i18n-inflector'
20
20
  # @private
@@ -3,12 +3,14 @@
3
3
  require 'i18n'
4
4
 
5
5
  require 'i18n-inflector/version'
6
- require 'i18n-inflector/errors'
7
- require 'i18n-inflector/util'
6
+ require 'i18n-inflector/lazy_enum'
7
+ require 'i18n-inflector/inflection_data_strict'
8
8
  require 'i18n-inflector/inflection_data'
9
9
  require 'i18n-inflector/options'
10
10
  require 'i18n-inflector/backend'
11
11
  require 'i18n-inflector/inflector'
12
+ require 'i18n-inflector/errors'
13
+ require 'i18n-inflector/api_strict'
14
+ require 'i18n-inflector/api'
12
15
 
13
16
  I18n::Backend::Simple.send(:include, I18n::Backend::Inflector)
14
-