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.
- data/.yardopts +1 -0
- data/ChangeLog +412 -0
- data/Gemfile +1 -0
- data/Manifest.txt +6 -1
- data/README.rdoc +16 -11
- data/Rakefile +15 -2
- data/ci/i18n-inflector.gemspec +1 -1
- data/ci/i18nv4-Gemfile +15 -0
- data/docs/EXAMPLES +222 -0
- data/docs/HISTORY +31 -0
- data/docs/LEGAL +0 -1
- data/docs/RELATIONS +16 -13
- data/docs/TODO +25 -3
- data/lib/i18n-inflector/api.rb +964 -0
- data/lib/i18n-inflector/api_strict.rb +519 -0
- data/lib/i18n-inflector/backend.rb +77 -40
- data/lib/i18n-inflector/errors.rb +56 -14
- data/lib/i18n-inflector/inflection_data.rb +133 -105
- data/lib/i18n-inflector/inflection_data_strict.rb +290 -0
- data/lib/i18n-inflector/inflector.rb +21 -660
- data/lib/i18n-inflector/lazy_enum.rb +120 -0
- data/lib/i18n-inflector/long_comments.rb +203 -14
- data/lib/i18n-inflector/version.rb +1 -1
- data/lib/i18n-inflector.rb +5 -3
- data/test/inflector_test.rb +334 -34
- data/test/test_helper.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +10 -6
- metadata.gz.sig +0 -0
- data/lib/i18n-inflector/util.rb +0 -67
@@ -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.
|
13
|
+
# @version 2.1
|
14
14
|
# This module contains inflection classes and modules for enabling
|
15
15
|
# the inflection support in I18n translations.
|
16
|
-
#
|
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
|
44
|
-
# in the scope called
|
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
|
80
|
-
# token it should pick. This
|
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.
|
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
|
91
|
-
#
|
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
|
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
|
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
|
data/lib/i18n-inflector.rb
CHANGED
@@ -3,12 +3,14 @@
|
|
3
3
|
require 'i18n'
|
4
4
|
|
5
5
|
require 'i18n-inflector/version'
|
6
|
-
require 'i18n-inflector/
|
7
|
-
require 'i18n-inflector/
|
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
|
-
|