i18n-inflector 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
-
|