i18n-inflector 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +1 -1
- data/ChangeLog +156 -0
- data/Gemfile +0 -1
- data/Manifest.txt +1 -1
- data/README.rdoc +21 -8
- data/ci/i18n-inflector.gemspec +1 -1
- data/docs/EXAMPLES +2 -2
- data/docs/HISTORY +16 -0
- data/docs/TODO +1 -1
- data/docs/USAGE +939 -0
- data/lib/i18n-inflector/api.rb +14 -14
- data/lib/i18n-inflector/backend.rb +2 -2
- data/lib/i18n-inflector/config.rb +17 -11
- data/lib/i18n-inflector/errors.rb +2 -2
- data/lib/i18n-inflector/inflector.rb +11 -0
- data/lib/i18n-inflector/interpolate.rb +77 -10
- data/lib/i18n-inflector/long_comments.rb +3 -783
- data/lib/i18n-inflector/version.rb +1 -1
- data/test/inflector_test.rb +57 -2
- data.tar.gz.sig +0 -0
- metadata +5 -5
- metadata.gz.sig +0 -0
- data/docs/RELATIONS +0 -28
data/docs/USAGE
ADDED
@@ -0,0 +1,939 @@
|
|
1
|
+
= Usage of the I18n Inflector
|
2
|
+
|
3
|
+
The {I18n::Inflector I18n Inflector} contains inflection classes
|
4
|
+
and modules for enabling the inflection support in I18n translations.
|
5
|
+
It is also used by the module called {I18n::Backend::Inflector}
|
6
|
+
that overwrites the translate method from the Simple backend
|
7
|
+
so it will interpolate additional inflection data present
|
8
|
+
in translations. That data may appear in *patterns*
|
9
|
+
enclosed within <tt>@{</tt> and <tt>}</tt> symbols. Each pattern
|
10
|
+
consist of *tokens* and respective *values*. One of the value will
|
11
|
+
be used depending on additional data passed to the translate method.
|
12
|
+
That additional data is called <b>inflection options</b>.
|
13
|
+
|
14
|
+
== Usage
|
15
|
+
require 'i18-inflector'
|
16
|
+
|
17
|
+
i18n.translate('welcome', :gender => :f)
|
18
|
+
# => Dear Madam
|
19
|
+
|
20
|
+
i18n.inflector.kinds
|
21
|
+
# => [:gender]
|
22
|
+
|
23
|
+
i18n.inflector.true_tokens.keys
|
24
|
+
# => [:f, :m, :n]
|
25
|
+
|
26
|
+
See the {file:EXAMPLES} for more information about real-life
|
27
|
+
usage of Inflector.
|
28
|
+
|
29
|
+
== Inflection pattern
|
30
|
+
An example inflection pattern stored under a translation key looks like:
|
31
|
+
welcome: "Dear @{f:Madam|m:Sir|n:You|All}"
|
32
|
+
|
33
|
+
The +f+, +m+ and +n+ are inflection *tokens* and +Madam+, +Sir+, +You+ and
|
34
|
+
+All+ are *values*. Only one value is going to replace the whole
|
35
|
+
pattern. To select which one an additional option is used. That option
|
36
|
+
must be passed to the translate method.
|
37
|
+
|
38
|
+
There are also so called <b>named patterns</b> that will be explained
|
39
|
+
later.
|
40
|
+
|
41
|
+
== Configuration
|
42
|
+
To recognize tokens present in patterns keys grouped
|
43
|
+
in the scope called +inflections+ for the given locale are used.
|
44
|
+
For instance (YAML format):
|
45
|
+
en:
|
46
|
+
i18n:
|
47
|
+
inflections:
|
48
|
+
gender:
|
49
|
+
f: "female"
|
50
|
+
m: "male"
|
51
|
+
n: "neuter"
|
52
|
+
man: @m
|
53
|
+
woman: @f
|
54
|
+
default: n
|
55
|
+
|
56
|
+
Elements in the example above are:
|
57
|
+
* +en+: language
|
58
|
+
* +i18n+: configuration scope
|
59
|
+
* +inflections+: inflections configuration scope
|
60
|
+
* +gender+: kind scope
|
61
|
+
* +f+, +m+, +n+: inflection tokens
|
62
|
+
* <tt>"male"</tt>, <tt>"female"</tt>, <tt>"neuter"</tt>: tokens' descriptions
|
63
|
+
* +woman+, +man+: inflection aliases
|
64
|
+
* <tt>@f</tt>, <tt>@m</tt>: pointers to real tokens
|
65
|
+
* +default+: default token for a kind +gender+
|
66
|
+
|
67
|
+
=== Kind
|
68
|
+
Note the fourth scope selector in the example above (+gender+). It's called
|
69
|
+
the *kind* and contains *tokens*. We have the kind
|
70
|
+
+gender+ to which the inflection tokens +f+, +m+ and +n+ are
|
71
|
+
assigned.
|
72
|
+
|
73
|
+
You cannot assign the same token to more than one kind.
|
74
|
+
Trying to do that will raise {I18n::DuplicatedInflectionToken} exception.
|
75
|
+
This is required in order to keep patterns simple and tokens interpolation
|
76
|
+
fast.
|
77
|
+
|
78
|
+
Kind is also used to instruct {I18n::Backend::Inflector#translate} method which
|
79
|
+
token it should pick. This is done through options and
|
80
|
+
will be explained later.
|
81
|
+
|
82
|
+
There is also a class of kind called <b>strict kind</b> used by
|
83
|
+
named patterns; that will be explained later.
|
84
|
+
|
85
|
+
=== Tokens
|
86
|
+
The token is an element of a pattern. Any pattern may have many tokens
|
87
|
+
of the same kind separated by vertical bars. Optionally tokens might
|
88
|
+
be groupped using commas (these are token groups; that will be explained
|
89
|
+
later). Each token name (or a group of tokens) used in a pattern should
|
90
|
+
end with colon sign. After this colon a value should appear (or an empty
|
91
|
+
string).
|
92
|
+
|
93
|
+
Tokens also appear in a configuration data. They are assigned to kinds.
|
94
|
+
Token names must be unique across all kinds, since it would be impossible
|
95
|
+
for interpolation routine to guess a kind of a token present in a pattern.
|
96
|
+
There is however a class of kinds called strict kinds, for which tokens
|
97
|
+
must be unique only within a kind. The named patterns that are using
|
98
|
+
strict kinds will be explained later.
|
99
|
+
|
100
|
+
=== Aliases
|
101
|
+
Aliases are special tokens that point to other tokens. By default they
|
102
|
+
cannot appear in inflection patterns but they are fully recognized
|
103
|
+
in options that are be passed to the translation method.
|
104
|
+
|
105
|
+
Aliases might be helpful in multilingual applications that are using
|
106
|
+
a fixed set of values passed through options to describe some properties
|
107
|
+
of messages, e.g. +masculine+ and +feminine+ for a grammatical gender.
|
108
|
+
Translators will then use their own tokens (like +f+ and +m+ for English)
|
109
|
+
to produce pretty and intuitive patterns.
|
110
|
+
|
111
|
+
For example: if some application uses database with gender assigned
|
112
|
+
to a user which may be +male+, +female+ or +none+, then a translator
|
113
|
+
may find it useful to map impersonal token (<tt>none</tt>)
|
114
|
+
to the +neuter+ token, since in translations for his language the
|
115
|
+
neuter gender is in use.
|
116
|
+
|
117
|
+
Here is the example of such situation:
|
118
|
+
|
119
|
+
en:
|
120
|
+
i18n:
|
121
|
+
inflections:
|
122
|
+
gender:
|
123
|
+
male: "male"
|
124
|
+
female: "female"
|
125
|
+
none: "impersonal form"
|
126
|
+
default: none
|
127
|
+
|
128
|
+
pl:
|
129
|
+
i18n:
|
130
|
+
inflections:
|
131
|
+
gender:
|
132
|
+
k: "female"
|
133
|
+
m: "male"
|
134
|
+
n: "neuter"
|
135
|
+
male: @k
|
136
|
+
female: @m
|
137
|
+
none: @n
|
138
|
+
default: none
|
139
|
+
|
140
|
+
In the case above Polish translator decided to use neuter
|
141
|
+
instead of impersonal form when +none+ token will be passed
|
142
|
+
through the option +:gender+ to the translate method. He
|
143
|
+
also decided that he will use +k+, +m+ or +n+ in patterns,
|
144
|
+
because the names are short and correspond to gender names in
|
145
|
+
Polish language: +k+ for 'kobieta' (woman), +m+ for 'mężczyzna' (man),
|
146
|
+
+n+ for 'nijaki' (neuter).
|
147
|
+
|
148
|
+
Aliases may point to other aliases. While loading inflections they
|
149
|
+
will be internally shortened and they will always point to real tokens,
|
150
|
+
not other aliases.
|
151
|
+
|
152
|
+
=== Default token
|
153
|
+
There is a special token called the +default+, which points
|
154
|
+
to a token that should be used if the interpolation routine cannot deduce
|
155
|
+
which one it should use because a proper option was not given.
|
156
|
+
|
157
|
+
Default tokens may point to aliases and may use aliases' syntax, e.g.:
|
158
|
+
default: @man
|
159
|
+
|
160
|
+
=== Descriptions
|
161
|
+
The values of keys in the example (+female+, +male+ and +neuter+)
|
162
|
+
are *descriptions* which usually are not used by the interpolation routine
|
163
|
+
but might be helpful (e.g. in UI). For obvious reasons you cannot
|
164
|
+
describe aliases.
|
165
|
+
|
166
|
+
== Interpolation
|
167
|
+
The value of each token present in a pattern is to be picked by the interpolation
|
168
|
+
routine and will replace the whole pattern, when a token name from that
|
169
|
+
pattern matches the given value of an option passed to the {I18n.translate} method.
|
170
|
+
|
171
|
+
=== Inflection option
|
172
|
+
The mentioned option is called the <b>inflection option</b>. Its name should be
|
173
|
+
the same as a *kind* of tokens used within a pattern. The first token in a pattern
|
174
|
+
determines the kind of all tokens used in that pattern. You can pass
|
175
|
+
many inflection options, each one designated for transporting a token of a
|
176
|
+
different kind.
|
177
|
+
|
178
|
+
==== Examples
|
179
|
+
|
180
|
+
*YAML:*
|
181
|
+
|
182
|
+
Let's assume that the translation data in YAML format listed
|
183
|
+
below is used in any later example, unless other inflections
|
184
|
+
are given.
|
185
|
+
en:
|
186
|
+
i18n:
|
187
|
+
inflections:
|
188
|
+
gender:
|
189
|
+
m: "male"
|
190
|
+
f: "female"
|
191
|
+
n: "neuter"
|
192
|
+
default: n
|
193
|
+
|
194
|
+
welcome: "Dear @{f:Madam|m:Sir|n:You|All}"
|
195
|
+
|
196
|
+
*Code:*
|
197
|
+
|
198
|
+
I18n.translate('welcome', :gender => :m)
|
199
|
+
# => "Dear Sir"
|
200
|
+
|
201
|
+
I18n.translate('welcome', :gender => :unknown)
|
202
|
+
# => "Dear All"
|
203
|
+
|
204
|
+
I18n.translate('welcome')
|
205
|
+
# => "Dear You"
|
206
|
+
|
207
|
+
In the second example the <b>fallback value</b> +All+ was interpolated
|
208
|
+
because the routine had been unable to find the token called +:unknown+.
|
209
|
+
That differs from the latest example, in which there was no option given,
|
210
|
+
so the default token for a kind had been applied (in this case +n+).
|
211
|
+
|
212
|
+
==== Inflection options as Methods or Procs
|
213
|
+
The inflection option may contain an object that is a kind of Method or
|
214
|
+
a Proc. The token will be obtained by calling the given method or a block when
|
215
|
+
interpolation routine will need it.
|
216
|
+
|
217
|
+
The passed method or a block should return an object that is a kind of
|
218
|
+
Symbol. It will be used as an inflection token.
|
219
|
+
|
220
|
+
Optionally the inflection method may make use of a block that is passed
|
221
|
+
to it. Currently two parameters can be obtained using the keyword +yield+.
|
222
|
+
Fist is the currenty parsed kind (including +@+ character in case of
|
223
|
+
a strict kind) and second is the locale; both are the kind of Symbol. Example:
|
224
|
+
|
225
|
+
def get_gender
|
226
|
+
kind, locale = yield # optional
|
227
|
+
:n # return token n
|
228
|
+
end
|
229
|
+
|
230
|
+
translate('welcome', :gender => method(:get_gender))
|
231
|
+
|
232
|
+
In case of Proc, the arguments are passed in a more comprehensive way,
|
233
|
+
as parameters passed to a block. Such a block must handle exactly
|
234
|
+
two arguments:
|
235
|
+
|
236
|
+
p = lambda{ |kind, locale| :m }
|
237
|
+
translate('welcome', :gender => p)
|
238
|
+
|
239
|
+
Note that if there will be any error that causes exception to be
|
240
|
+
raised by such a method or a block then it will be raised regardless
|
241
|
+
of +:inflector_raises+ option.
|
242
|
+
|
243
|
+
=== Local fallbacks (free text)
|
244
|
+
The fallback value will be used when none of the tokens contained
|
245
|
+
within a pattern can be interpolated.
|
246
|
+
|
247
|
+
Be aware that enabling extended error reporting makes it unable
|
248
|
+
to use fallback values in most cases. Local fallbacks will then be
|
249
|
+
applied only when the given option contains a proper value for some
|
250
|
+
kind but it's just not present in a pattern, for example:
|
251
|
+
|
252
|
+
*YAML:*
|
253
|
+
|
254
|
+
en:
|
255
|
+
i18n:
|
256
|
+
inflections:
|
257
|
+
gender:
|
258
|
+
n: 'neuter'
|
259
|
+
o: 'other'
|
260
|
+
|
261
|
+
welcome: "Dear @{n:You|All}"
|
262
|
+
|
263
|
+
*Code:*
|
264
|
+
|
265
|
+
I18n.translate('welcome', :gender => :o, :inflector_raises => true)
|
266
|
+
# => "Dear All"
|
267
|
+
# since the token :o was configured but not used in the pattern
|
268
|
+
|
269
|
+
=== Bad and empty tokens in options
|
270
|
+
If an option containing token is not present at all then the interpolation
|
271
|
+
routine will try the default token for a processed kind, if the default
|
272
|
+
token is present in a pattern. The same thing will happend if the option
|
273
|
+
is present but its value is malformed, unknown, empty or +nil+.
|
274
|
+
If the default token is not present in a pattern or is not defined in
|
275
|
+
a configuration data then the processing of a pattern will result
|
276
|
+
in an empty string or in a local fallback value if there is
|
277
|
+
a free text placed in a pattern.
|
278
|
+
|
279
|
+
You can change this default behavior and force inflector
|
280
|
+
not to use a default token when a value of an option for
|
281
|
+
a kind is malformed, unknown, empty or +nil+ but only when
|
282
|
+
it's not present. To do that you should set option +:inflector_unknown_defaults+
|
283
|
+
to +false+ and pass it to the translate method. Other way is to set this
|
284
|
+
switch globally using the {I18n::Inflector::InflectionOptions#unknown_defaults}.
|
285
|
+
|
286
|
+
=== Unmatched tokens in options
|
287
|
+
It might happend that there will be a default token present in a pattern
|
288
|
+
but the given inflection option will cause some other token to be picked up,
|
289
|
+
which however won't be present in this pattern (although it will be
|
290
|
+
correct and assigned to the currently processed kind). In such
|
291
|
+
case the given free text or an empty string will be generated.
|
292
|
+
You may change that behavior by passing +:inflector_excluded_defaults+
|
293
|
+
option set to +true+ or by setting the global option called
|
294
|
+
{I18n::Inflector::InflectionOptions#excluded_defaults}. If this
|
295
|
+
option is set then any unmatched (excluded but correct) token
|
296
|
+
given in an inflection option will cause the default token's value
|
297
|
+
to be picked up (of course if a default token will be present
|
298
|
+
in a pattern).
|
299
|
+
|
300
|
+
=== Mixing inflection and standard interpolation patterns
|
301
|
+
The Inflector allows you to include standard <tt>%{}</tt>
|
302
|
+
patterns inside of inflection patterns. The value of a standard
|
303
|
+
interpolation variable will be evaluated and interpolated *before*
|
304
|
+
processing an inflection pattern. For example:
|
305
|
+
|
306
|
+
*YAML:*
|
307
|
+
|
308
|
+
Note: <em>Uses inflection configuration given in the first example.</em>
|
309
|
+
en:
|
310
|
+
hi: "Dear @{f:Lady|m:%{test}}!"
|
311
|
+
|
312
|
+
*Code:*
|
313
|
+
I18n.t('hi', :gender => :m, :locale => :xx, :test => "Dude")
|
314
|
+
# => Dear Dude!
|
315
|
+
|
316
|
+
=== Token groups
|
317
|
+
It is possible to assign some value to more than one token in a patterns.
|
318
|
+
You can create group of tokens by separating them using commas.
|
319
|
+
The comma has the meaning of logical OR.
|
320
|
+
|
321
|
+
*YAML:*
|
322
|
+
|
323
|
+
Note: <em>Uses inflection configuration given in the first example.</em>
|
324
|
+
en:
|
325
|
+
welcome: "Hello @{m,f:Ladies and Gentlemen|n:You}!"
|
326
|
+
|
327
|
+
*Code:*
|
328
|
+
I18n.t('welcome', :gender => :f)
|
329
|
+
# => Hello Ladies and Gentlemen!
|
330
|
+
|
331
|
+
=== Inversed matching of tokens
|
332
|
+
You can place exclamation mark before a token that should be
|
333
|
+
matched negatively. Its value will be used for a pattern
|
334
|
+
<b>if the given inflection option contains other token</b>.
|
335
|
+
You can use inversed matching of tokens in token groups but
|
336
|
+
note that putting more than one inversed token to a group
|
337
|
+
will cause the expression to mach every time.
|
338
|
+
|
339
|
+
*YAML:*
|
340
|
+
|
341
|
+
Note: <em>Uses inflection configuration given in the first example.</em>
|
342
|
+
en:
|
343
|
+
welcome: "Hello @{!m:Ladies|n:You}!"
|
344
|
+
|
345
|
+
*Code:*
|
346
|
+
|
347
|
+
I18n.t('welcome', :gender => :n)
|
348
|
+
# => Hello Ladies!
|
349
|
+
|
350
|
+
I18n.t('welcome', :gender => :f)
|
351
|
+
# => Hello Ladies!
|
352
|
+
|
353
|
+
I18n.t('welcome', :gender => :m)
|
354
|
+
# => Hello !
|
355
|
+
|
356
|
+
=== Wildcard tokens
|
357
|
+
You may use the wildcard character, a star (+*+), in place of token
|
358
|
+
group to create a virtual token that matches any token of a parsed kind.
|
359
|
+
For example:
|
360
|
+
|
361
|
+
*YAML:*
|
362
|
+
|
363
|
+
Note: <em>Uses inflection configuration given in the first example.</em>
|
364
|
+
|
365
|
+
en:
|
366
|
+
welcome: "Hello @{n:you|*:ladies and gentlemen}!"
|
367
|
+
|
368
|
+
*Code:*
|
369
|
+
|
370
|
+
I18n.t('welcome', :gender => :n)
|
371
|
+
# => Hello you!
|
372
|
+
|
373
|
+
I18n.t('welcome', :gender => :f)
|
374
|
+
# => Hello ladies and gentlemen!
|
375
|
+
|
376
|
+
Note that for simple patterns you can use free text instead, which works
|
377
|
+
almost the same way with one significant difference: free text will be
|
378
|
+
evaluated as the last expression, regardless of its placement.
|
379
|
+
On the contrary a wildcard token will be evaluated as any other
|
380
|
+
token group and may not be used if any previously tested token
|
381
|
+
will match (like +n+ in the example above).
|
382
|
+
|
383
|
+
While a wildcard token is processed then the interpolation routine
|
384
|
+
will validate if the required inflection option exists and if it contains
|
385
|
+
a proper token. Using wildcard token is like using a token group
|
386
|
+
for any other token group containing all possible true tokens in it.
|
387
|
+
|
388
|
+
In case of regular patterns containing just a wildcard token alone
|
389
|
+
there is no way to easily decide which kind the expression
|
390
|
+
refers to. To deduce it the first valid inflection option will
|
391
|
+
be used. In order to work it must contain some valid token
|
392
|
+
identifier. If the token identifier is invalid and there are more
|
393
|
+
inflection options then they are tried.
|
394
|
+
|
395
|
+
Wildcard tokens are useful in so called complex patterns which
|
396
|
+
will be explained later.
|
397
|
+
|
398
|
+
=== Loud tokens
|
399
|
+
Sometimes there might be a need to use descriptions of
|
400
|
+
matching tokens instead of some given values. Use <b>loud tokens</b>
|
401
|
+
to achieve that. Any matching token in a pattern that has tilde symbol (+~+)
|
402
|
+
set as its value will be replaced by its description. In case of
|
403
|
+
undescribed aliases, the description from a target token will be used.
|
404
|
+
|
405
|
+
*YAML:*
|
406
|
+
|
407
|
+
Note: <em>Uses inflection configuration given in the first example.</em>
|
408
|
+
en:
|
409
|
+
welcome: "Hello @{m:~|n:~}!"
|
410
|
+
|
411
|
+
*Code:*
|
412
|
+
|
413
|
+
I18n.t('welcome', :gender => :n)
|
414
|
+
# => Hello neuter!
|
415
|
+
|
416
|
+
I18n.t('welcome', :gender => :f)
|
417
|
+
# => Hello female!
|
418
|
+
|
419
|
+
To use tilde symbol as the only value of a token you may esape it
|
420
|
+
by putting a backslash in front of the symbol.
|
421
|
+
|
422
|
+
Using loud token with wildcard token will result in a description
|
423
|
+
of first matching token.
|
424
|
+
|
425
|
+
=== Aliases in a pattern
|
426
|
+
Normally it is possible to use in patterns only true tokens, not aliases.
|
427
|
+
However, if you feel lucky and you're not affraid of messy patterns
|
428
|
+
you can use the switch {I18n::Inflector::InflectionOptions#aliased_patterns}
|
429
|
+
or pass corresponding +:inflector_aliased_patterns+ option to the translate
|
430
|
+
method.
|
431
|
+
|
432
|
+
=== Escaping a pattern
|
433
|
+
If there is a need to translate something that accidentally matches
|
434
|
+
an inflection pattern then the escape symbols can be used to
|
435
|
+
disable the interpolation. These symbols are <tt>\\</tt> and +@+
|
436
|
+
and they should be placed just before a pattern that should
|
437
|
+
be left untouched. For instance:
|
438
|
+
|
439
|
+
*YAML:*
|
440
|
+
|
441
|
+
Note: <em>Uses inflection configuration given in the first example.</em>
|
442
|
+
en:
|
443
|
+
welcome: "This is the @@{pattern}!"
|
444
|
+
|
445
|
+
*Code:*
|
446
|
+
|
447
|
+
I18n.t('welcome', :gender => :m, :locale => :xx)
|
448
|
+
# => This is the @{pattern}!
|
449
|
+
|
450
|
+
=== More about applying aliases
|
451
|
+
It may seem very easy and attractive to use aliases
|
452
|
+
in environments where inflection option's value comes from a user.
|
453
|
+
In such cases aliases may be used as database that translates common
|
454
|
+
words to inflection tokens that have meanings. For example a user may
|
455
|
+
enter a gender in some text field and it will be used as value of inflection
|
456
|
+
option. To map different names (e.g. male, boy, sir, female, girl, lady)
|
457
|
+
to exact inflection tokens the aliases would be used.
|
458
|
+
|
459
|
+
Note hovewer, that you can make use of <tt>I18n.inflector.true_token</tt>
|
460
|
+
method (see {I18n::Inflector::API#true_token}) that will resolve any alias,
|
461
|
+
and then use that data to feed an inflection option (e.g. +:gender+).
|
462
|
+
In such scenario you don't have to rely on resolving aliases
|
463
|
+
any time translation is performed and you will gain some speed.
|
464
|
+
|
465
|
+
== Named patterns
|
466
|
+
|
467
|
+
A named pattern is a pattern that contains name of a kind
|
468
|
+
that tokens from a pattern are assigned to. It looks like:
|
469
|
+
|
470
|
+
welcome: "Dear @gender{f:Madam|m:Sir|n:You|All}"
|
471
|
+
|
472
|
+
=== Configuring named patterns
|
473
|
+
To recognize tokens present in named patterns,
|
474
|
+
inflector uses keys grouped in the scope called +inflections+
|
475
|
+
for the given locale. For instance (YAML format):
|
476
|
+
en:
|
477
|
+
i18n:
|
478
|
+
inflections:
|
479
|
+
@gender:
|
480
|
+
f: "female"
|
481
|
+
woman: @f
|
482
|
+
default: f
|
483
|
+
|
484
|
+
Elements in the example above are:
|
485
|
+
* +en+: language
|
486
|
+
* +i18n+: configuration scope
|
487
|
+
* +inflections+: inflections configuration scope
|
488
|
+
* +gender+: <bb>strict kind</bb> scope
|
489
|
+
* +f+: inflection token
|
490
|
+
* <tt>"female"</tt>: token's description
|
491
|
+
* +woman+: inflection alias
|
492
|
+
* <tt>@f</tt>: pointer to real token
|
493
|
+
* +default+: default token for a strict kind +gender+
|
494
|
+
|
495
|
+
=== Strict kinds
|
496
|
+
|
497
|
+
In order to handle named patterns properly a new data structure
|
498
|
+
is used. It is called the <b>strict kind</b>. Strict kinds are defined
|
499
|
+
in a configuration in a similar way the regular kinds are but
|
500
|
+
tokens assigned to them may have the same names across a whole
|
501
|
+
configuration. (Note that tokens of the same strict kind should still
|
502
|
+
be unique.) That implies a requirement of passing the
|
503
|
+
identifier of a kind in patterns when referring to such tokens.
|
504
|
+
|
505
|
+
Here is the example configuration using strict kinds:
|
506
|
+
|
507
|
+
en:
|
508
|
+
i18n:
|
509
|
+
inflections:
|
510
|
+
@gender:
|
511
|
+
f: "female"
|
512
|
+
m: "male"
|
513
|
+
n: "neuter"
|
514
|
+
man: @m
|
515
|
+
woman: @f
|
516
|
+
default: n
|
517
|
+
@title:
|
518
|
+
s: "sir"
|
519
|
+
l: "lady"
|
520
|
+
u: "you"
|
521
|
+
m: @s
|
522
|
+
f: @l
|
523
|
+
default: u
|
524
|
+
|
525
|
+
The only thing that syntactically distinguishes strict kinds
|
526
|
+
from regular kinds is a presence of the +@+ symbol.
|
527
|
+
|
528
|
+
You can mix regular and strict kinds having the same names in
|
529
|
+
one translation entry, but not in one inflection pattern.
|
530
|
+
The proper class of kind will be picked up by interpolation
|
531
|
+
method easily, since the first mentioned class uses
|
532
|
+
patterns that are not named, and the second uses named patterns.
|
533
|
+
|
534
|
+
==== Strict kinds in inflection options
|
535
|
+
The interpolation routine recognizes strict kinds passed as names of
|
536
|
+
inflection options in almost the same way that it does for regular
|
537
|
+
kinds. The only difference is that you can override usage
|
538
|
+
of a regular kind inflection option (if there is any) by
|
539
|
+
putting a strict kind option with the same name but prefixed by +@+ symbol.
|
540
|
+
The inflection options starting with this symbol have
|
541
|
+
precedence over inflection options without it;
|
542
|
+
that is of course only true for strict kinds and has any effect
|
543
|
+
only when both options describing kinds of the same name are present.
|
544
|
+
|
545
|
+
In other words: interpolation routine is looking for
|
546
|
+
strict kinds in inflection options using their names
|
547
|
+
with +@+ in front. When that fails it falls back to
|
548
|
+
an option named like the strict kind but without
|
549
|
+
the +@+ symbol. Examples:
|
550
|
+
|
551
|
+
I18n.translate(welcome, :gender => :m, :@gender => :f)
|
552
|
+
# the :f will be picked for the strict kind gender
|
553
|
+
|
554
|
+
I18n.translate(welcome, :@gender => :f)
|
555
|
+
# the :f will be picked for the strict kind gender
|
556
|
+
|
557
|
+
I18n.translate(welcome, :gender => :f)
|
558
|
+
# the :f will be picked for the strict kind gender
|
559
|
+
|
560
|
+
In the example above we assume that +welcome+ is defined
|
561
|
+
like that:
|
562
|
+
|
563
|
+
welcome: "Dear @gender{f:Madam|m:Sir|n:You|All}"
|
564
|
+
|
565
|
+
Note that for regular kinds the option named +:@gender+
|
566
|
+
will have no meaning.
|
567
|
+
|
568
|
+
==== Note for developers
|
569
|
+
|
570
|
+
Strict kinds that are used to handle named patterns
|
571
|
+
are internally stored in a different database and handled by
|
572
|
+
similar but different API methods than regular kinds. However
|
573
|
+
most of the {I18n::Inflector::API} methods are also aware of strict kinds
|
574
|
+
and will call proper methods oprating on strict inflections
|
575
|
+
data when the +@+ symbol is detected at the beginning of
|
576
|
+
the identifier of a kind passed as an argument. For example:
|
577
|
+
|
578
|
+
I18n.inflector.has_token?(:m, :@gender)
|
579
|
+
|
580
|
+
will effectively call:
|
581
|
+
|
582
|
+
I18n.inflector.strict.has_token?(:m, :gender)
|
583
|
+
|
584
|
+
As you can see above, to access {API_Strict} methods for strict kinds
|
585
|
+
(and strict kinds data) only, associated with default I18n backend,
|
586
|
+
use:
|
587
|
+
|
588
|
+
I18n.inflector.strict
|
589
|
+
|
590
|
+
== Multiple patterns
|
591
|
+
You can make use of some syntactic sugar when having more than
|
592
|
+
one pattern (regular or named) in your string. To not repeat
|
593
|
+
a kind identifier(s) you may join pattern contents as in the
|
594
|
+
following example:
|
595
|
+
|
596
|
+
welcome: "You are @gender{f:pretty|m,n:handsome}{ }{f:lady|m:sir|n:human}"
|
597
|
+
|
598
|
+
As you can see there should be no spaces or any other characters
|
599
|
+
between successive patterns. That's why in this example an
|
600
|
+
empty pattern content is used. This is in fact a pattern
|
601
|
+
containing no tokens but just a free text consisting
|
602
|
+
of single space character.
|
603
|
+
|
604
|
+
== Complex patterns
|
605
|
+
A <bb>complex pattern</bb> is a named pattern that uses more than
|
606
|
+
one inflection kind and sets of a respective tokens. The given identifiers
|
607
|
+
of kinds should be separated by the plus sign and instead of single
|
608
|
+
tokens there should be token sets (a tokens separated by the plus
|
609
|
+
sign too).
|
610
|
+
|
611
|
+
Example:
|
612
|
+
|
613
|
+
welcome: "Dear @gender+number{f+s:Lady|f+p:Ladies|m+s:Sir|m+p:Gentlemen|All}"
|
614
|
+
|
615
|
+
In the example above the complex pattern uses +gender+ and +number+
|
616
|
+
inflection kinds and a token set (e.g. <tt>f+s</tt>) matches when
|
617
|
+
both tokens match interpolation options (e.g. <tt>:gender => :f</tt>,
|
618
|
+
<tt>:number => :s</tt>). The order of tokens in sets has meaning
|
619
|
+
and should reflect the order of declared kinds.
|
620
|
+
|
621
|
+
Note, that the count of tokens in each set should reflect the count
|
622
|
+
of kinds that are used. Otherwise the interpolation routine will
|
623
|
+
interpolate a free text (if given) or an empty string. If the switch
|
624
|
+
{InflectionOptions#raises} is on then the {I18n::ComplexPatternMalformed}
|
625
|
+
exception will be raised.
|
626
|
+
|
627
|
+
The inflection tokens used in sets may make use of any features mentioned
|
628
|
+
before (defaults, excluded defaults, negative matching, token groups,
|
629
|
+
aliases, aliased patterns, loud tokens, wildcards).
|
630
|
+
|
631
|
+
=== Loud tokens in complex patterns
|
632
|
+
In case of loud tokens (having values taken from their
|
633
|
+
descriptions), the complex pattern will be replaced by
|
634
|
+
the descriptions of matching tokens joined with a single space
|
635
|
+
character. So, for instance, when the translation data looks like:
|
636
|
+
|
637
|
+
i18n:
|
638
|
+
inflections:
|
639
|
+
@person:
|
640
|
+
i: 'I'
|
641
|
+
u: 'You'
|
642
|
+
@tense:
|
643
|
+
now: 'am'
|
644
|
+
past: 'were'
|
645
|
+
welcome: "@person+tense{i+now:~|u+past:~}"
|
646
|
+
|
647
|
+
the translate method will give the following results:
|
648
|
+
|
649
|
+
I18n.translate('welcome', :person => :i, :tense => :now)
|
650
|
+
# => "I am"
|
651
|
+
|
652
|
+
I18n.translate('welcome', :person => :you, :tense => :past)
|
653
|
+
# => "You were"
|
654
|
+
|
655
|
+
This example is abstract, since the combination of +:i+
|
656
|
+
and +:past+ will result in <tt>i were</tt> string, which is
|
657
|
+
probably something unexpected. To achieve that kind of logic
|
658
|
+
simply use combined patterns with the given values instead
|
659
|
+
of loud tokens.
|
660
|
+
|
661
|
+
=== Wildcard tokens in complex patterns
|
662
|
+
The wildcard tokens might be extremely helpful in complex
|
663
|
+
patterns since there is one shared free text for a whole pattern
|
664
|
+
yet there might be a need to match any token for some subkind.
|
665
|
+
For example:
|
666
|
+
|
667
|
+
welcome: @person+tense{i+present:am|u+present:are|*+present:is}
|
668
|
+
|
669
|
+
Note that in the example above +*+ matches 'i', 'you', 'he', 'she' and 'it'
|
670
|
+
but 'i' and 'u' are effectively matched before. The equivalent pattern without
|
671
|
+
a wildcard token would look like:
|
672
|
+
|
673
|
+
welcome: @person+tense{i+present:am|u+present:are|i,u,he,she,it+present:is}
|
674
|
+
|
675
|
+
== Inflection keys
|
676
|
+
There is a way of storing inflected strings in keys instead
|
677
|
+
of patterns. To use it you should simply assign subkeys to
|
678
|
+
some translation key instead of string containing a pattern.
|
679
|
+
The key-based inflection group is contained within a key
|
680
|
+
which name begins with the +@+ symbol.
|
681
|
+
|
682
|
+
The translation key containing a pattern:
|
683
|
+
|
684
|
+
welcome: "Dear @{f:Lady|m:Sir|n:You|All}!"
|
685
|
+
|
686
|
+
Can be easily written as:
|
687
|
+
|
688
|
+
@welcome:
|
689
|
+
f: "Lady"
|
690
|
+
m: "Sir"
|
691
|
+
n: "You"
|
692
|
+
@free: "All"
|
693
|
+
@prefix: "Dear "
|
694
|
+
@suffix: "!"
|
695
|
+
|
696
|
+
You can also use strict kind or even the inflection sets, token
|
697
|
+
groups, etc.:
|
698
|
+
|
699
|
+
welcome: "@gender+tense{f+past:She was|m+present:He is|n+future:You will be}"
|
700
|
+
|
701
|
+
Can be written as:
|
702
|
+
|
703
|
+
@welcome:
|
704
|
+
f+past: "She was"
|
705
|
+
m+present: "He is"
|
706
|
+
n+future: "You will be"
|
707
|
+
@kind: "gender+tense"
|
708
|
+
|
709
|
+
There are special, optional subkeys that may give you
|
710
|
+
more control over inflection process. These are:
|
711
|
+
|
712
|
+
* +@kind+: a kind or kinds in case of strict kinds
|
713
|
+
* +@prefix+: a prefix to be put before the interpolated data
|
714
|
+
* +@suffix+: a suffix to be put after the interpolated data
|
715
|
+
* +@free+: a free text that is to be used when no token will match
|
716
|
+
|
717
|
+
=== Limitations
|
718
|
+
|
719
|
+
Inflection keys look compact and clean but obviously
|
720
|
+
you cannot use the key-based inflection to simply replace
|
721
|
+
a string containing more than one inflection pattern.
|
722
|
+
|
723
|
+
Also, <b>you have to be very careful when using this method
|
724
|
+
with Ruby 1.8</b> because the order of processed token sets
|
725
|
+
may change. That may break the logic in case of inflection
|
726
|
+
sets where order has meaning (e.g. tokens with inverted
|
727
|
+
matching).
|
728
|
+
|
729
|
+
== Errors
|
730
|
+
By default the module will silently ignore non-critical interpolation
|
731
|
+
errors. You can turn off this default behavior by passing +:inflector_raises+
|
732
|
+
option set to +true+. Note that most errors is reported because of
|
733
|
+
wrong data in patterns or in configuration. In case of inflection
|
734
|
+
options only malformed, empty or +nil+ values are reported
|
735
|
+
when the mentioned switch is turned on. For inflection options
|
736
|
+
containing unknown tokens no errors are generated.
|
737
|
+
|
738
|
+
=== Usage of +:inflector_raises+ option
|
739
|
+
|
740
|
+
*YAML:*
|
741
|
+
|
742
|
+
Note: <em>Uses inflection configuration given in the first example.</em>
|
743
|
+
en:
|
744
|
+
welcome: "Dear @{m:Sir|f:Madam|Fallback}"
|
745
|
+
|
746
|
+
*Code:*
|
747
|
+
|
748
|
+
I18n.t('welcome', :inflector_raises => true)
|
749
|
+
# => I18n::InflectionOptionNotFound: en.welcome:
|
750
|
+
# @{m:Sir|f:Madam|Fallback}" - required option :gender was not found
|
751
|
+
|
752
|
+
=== Exception meanings
|
753
|
+
Here are the exceptions that may be raised when the option +:inflector_raises+
|
754
|
+
is set to +true+:
|
755
|
+
|
756
|
+
* {I18n::InvalidInflectionToken I18n::InvalidInflectionToken}
|
757
|
+
* {I18n::InvalidInflectionKind I18n::InvalidInflectionKind}
|
758
|
+
* {I18n::InvalidInflectionOption I18n::InvalidInflectionOption}
|
759
|
+
* {I18n::MisplacedInflectionToken I18n::MisplacedInflectionToken}
|
760
|
+
* {I18n::InflectionOptionNotFound I18n::InflectionOptionNotFound}
|
761
|
+
* {I18n::InflectionOptionIncorrect I18n::InflectionOptionIncorrect}
|
762
|
+
* {I18n::ComplexPatternMalformed I18n::ComplexPatternMalformed}
|
763
|
+
|
764
|
+
There are also exceptions that are raised regardless of :+inflector_raises+
|
765
|
+
presence or value.
|
766
|
+
These are usually caused by critical errors encountered during processing
|
767
|
+
inflection data or exceptions raised by I18n. Note that the pure I18n's
|
768
|
+
exceptions are not described here.
|
769
|
+
|
770
|
+
* {I18n::ArgumentError I18n::ArgumentError}
|
771
|
+
* {I18n::InvalidLocale I18n::InvalidLocale}
|
772
|
+
* {I18n::DuplicatedInflectionToken I18n::DuplicatedInflectionToken}
|
773
|
+
* {I18n::BadInflectionKind I18n::BadInflectionKind}
|
774
|
+
* {I18n::BadInflectionToken I18n::BadInflectionToken}
|
775
|
+
* {I18n::BadInflectionAlias I18n::BadInflectionAlias}
|
776
|
+
|
777
|
+
=== Exception hierarchy
|
778
|
+
I18n::ArgumentError
|
779
|
+
|
|
780
|
+
`-- I18n::InvalidLocale
|
781
|
+
|
|
782
|
+
`-- I18n::InflectionException
|
783
|
+
|
|
784
|
+
`-- I18n::InflectionPatternException
|
785
|
+
| |
|
786
|
+
| |-- I18n::InvalidInflectionToken
|
787
|
+
| |-- I18n::InvalidInflectionKind
|
788
|
+
| |-- I18n::MisplacedInflectionToken
|
789
|
+
| |-- I18n::ComplexPatternMalformed
|
790
|
+
| `-- I18n::InvalidOptionForKind
|
791
|
+
| |-- I18n::InflectionOptionNotFound
|
792
|
+
| `-- I18n::InflectionOptionIncorrect
|
793
|
+
|
|
794
|
+
`-- I18n::InflectionConfigurationException
|
795
|
+
|
|
796
|
+
|-- I18n::DuplicatedInflectionToken
|
797
|
+
|-- I18n::BadInflectionAlias
|
798
|
+
|-- I18n::BadInflectionToken
|
799
|
+
`-- I18n::BadInflectionKind
|
800
|
+
|
801
|
+
== Reserved names and characters
|
802
|
+
Some strings cannot be used as names and/or identifiers of
|
803
|
+
kinds and tokens. There are also some reserved characters
|
804
|
+
that cannot be used within them.
|
805
|
+
|
806
|
+
=== Reserved keys
|
807
|
+
Reserved keys, that cannot be used as names of inflection
|
808
|
+
options and as names of kinds in the configuration
|
809
|
+
are available after issuing:
|
810
|
+
|
811
|
+
I18n::Inflector::Config::Reserved::KEYS.to_a
|
812
|
+
|
813
|
+
Here is the current list: <tt>:scope, :default, :separator,
|
814
|
+
:resolve, :object, :fallback, :format, :cascade,
|
815
|
+
:raise, :rescue_format, :inflector_cache_aware,
|
816
|
+
:inflector_raises, :inflector_aliased_patterns,
|
817
|
+
:inflector_unknown_defaults, :inflector_excluded_defaults</tt>.
|
818
|
+
|
819
|
+
Additionally all Symbols or Strings beginning with
|
820
|
+
+inflector_+ are prohibited, since they are reserved as
|
821
|
+
controlling options.
|
822
|
+
|
823
|
+
=== Reserved characters
|
824
|
+
All characters that have special meaning (operators and
|
825
|
+
markers) are not allowed in patterns, in configuration
|
826
|
+
and in options.
|
827
|
+
|
828
|
+
==== Reserved characters in kinds
|
829
|
+
===== Passed as inflection options
|
830
|
+
|
831
|
+
* *Constant:* {I18n::Inflector::Config::Reserved::Kinds::OPTION}
|
832
|
+
|
833
|
+
* *List:* <tt>+ | : ! { }</tt> and <tt>,</tt> (comma).
|
834
|
+
|
835
|
+
===== Given in a configuration
|
836
|
+
|
837
|
+
* *Constant:* {I18n::Inflector::Config::Reserved::Kinds::DB}
|
838
|
+
|
839
|
+
* *List:* <tt>+ | : ! { }</tt> and <tt>,</tt> (comma).
|
840
|
+
|
841
|
+
===== Placed in patterns
|
842
|
+
|
843
|
+
* *Constant:* {I18n::Inflector::Config::Reserved::Kinds::PATTERN}
|
844
|
+
|
845
|
+
* *List:* <tt>+ | : , ! @ { }</tt> and <tt>,</tt> (comma).
|
846
|
+
|
847
|
+
==== Reserved characters in tokens
|
848
|
+
===== Passed as values of inflection options
|
849
|
+
|
850
|
+
* *Constant:* {I18n::Inflector::Config::Reserved::Tokens::OPTION}
|
851
|
+
|
852
|
+
* *List:* <tt>* + | : ! @ { }</tt> and <tt>,</tt> (comma).
|
853
|
+
|
854
|
+
===== Given in a configuration
|
855
|
+
|
856
|
+
* *Constant:* {I18n::Inflector::Config::Reserved::Tokens::DB}
|
857
|
+
|
858
|
+
* *List:* <tt>* + | : ! @ { }</tt> and <tt>,</tt> (comma).
|
859
|
+
|
860
|
+
===== Placed in patterns
|
861
|
+
|
862
|
+
* *Constant:* {I18n::Inflector::Config::Reserved::Tokens::PATTERN}
|
863
|
+
|
864
|
+
* *List:* <tt>+ | : ! @ { }</tt> and <tt>,</tt> (comma).
|
865
|
+
|
866
|
+
== Operators and markers
|
867
|
+
Here is more formal definition of operators and markers used in patterns.
|
868
|
+
|
869
|
+
=== Pattern
|
870
|
+
@[kind][+kind ...]{token_set[|token_set ...][|free_text]}
|
871
|
+
|
872
|
+
* +@+ is the pattern marker
|
873
|
+
* +{+ and +}+ are pattern delimiters
|
874
|
+
* +free_text+ is an optional free text value
|
875
|
+
* +kind+ is a kind identifier
|
876
|
+
* <tt>+</tt> is the +AND+ operator that joins kinds (produces complex kinds)
|
877
|
+
|
878
|
+
==== +token_set+
|
879
|
+
*|token_group[+token_group ...]:value
|
880
|
+
|
881
|
+
* +:+ is the +ASSIGNMENT+ operator
|
882
|
+
* +value+ is a value to be picked up then a token set matches; value may also
|
883
|
+
be the loud marker (+~+)
|
884
|
+
* <tt>+</tt> is the +AND+ operator that joins many token groups into a set
|
885
|
+
* +*+ is the +WILDCARD+ operator
|
886
|
+
|
887
|
+
===== +token_group+
|
888
|
+
[!]token[,[!]token ...]
|
889
|
+
|
890
|
+
* +token+ is a token identifier
|
891
|
+
* +!+ is the +NOT+ operator
|
892
|
+
* +,+ is the +OR+ operator
|
893
|
+
|
894
|
+
=== Operator precedence
|
895
|
+
|
896
|
+
* Single token level
|
897
|
+
* +NOT+ operators for inversed matching of tokens (<tt>!</tt>)
|
898
|
+
* +OR+ operators for joining tokens into token groups (<tt>,</tt>)
|
899
|
+
* Token group level
|
900
|
+
* +WILDCARD+ operators for matching any token (<tt>*</tt>)
|
901
|
+
* +AND+ operators for joining token groups into sets (<tt>+</tt>)
|
902
|
+
* Token set level
|
903
|
+
* +ASSIGNMENT+ operators for assigning values to token sets (<tt>:</tt>)
|
904
|
+
* +OR+ operators for separating token sets and/or free texts (<tt>|</tt>)
|
905
|
+
* Pattern name level
|
906
|
+
* +AND+ operators for kind identifiers (<tt>+</tt>)
|
907
|
+
* Pattern level
|
908
|
+
* Pattern marker and pattern delimiters
|
909
|
+
|
910
|
+
== Classes and relations
|
911
|
+
|
912
|
+
=== Library contents
|
913
|
+
|
914
|
+
* Module {I18n::Inflector} containing everything
|
915
|
+
* Class {I18n::Inflector::API} used to create inflector object attached to I18n backend (handles regular and strict kinds)
|
916
|
+
* Class {I18n::Inflector::API_Strict} which instance is attached to {I18n::Inflector::API} and handles strict kinds
|
917
|
+
* Class {I18n::Inflector::InflectionData} used to store inflection data for regular kinds and tokens
|
918
|
+
* Class {I18n::Inflector::InflectionData_Strict} used to store inflection data for strict kinds and tokens
|
919
|
+
* Class {I18n::Inflector::InflectionOptions} used for keeping switches and options
|
920
|
+
* Class {I18n::Inflector::LazyHashEnumerator} used to manage lazy evaluation of internal data
|
921
|
+
* Module {I18n::Backend::Inflector} used to alter methods of {I18n::Backend::Simple}
|
922
|
+
* Several classes for error reporting
|
923
|
+
|
924
|
+
=== Relations
|
925
|
+
|
926
|
+
* {I18n.backend} is the currently used backend and the instance of {I18n::Backend::Simple}
|
927
|
+
* {I18n.backend.inflector} is the instance of {I18n::Inflector::API} attached to backend
|
928
|
+
* {I18n.inflector} is the proxy module method that calls inflector for currently used backend {I18n.backend.inflector}
|
929
|
+
* {I18n.backend.inflector.options} is the instance of {I18n::Inflector::InflectionOptions} and
|
930
|
+
mainly it controls a behavior of interpolation method
|
931
|
+
* {I18n.backend.inflector.strict} is the instance of {I18n::Inflector::API_Strict} and handles strict kinds
|
932
|
+
* {I18n.backend.inflector} uses {I18n.backend.inflector.strict} to access strict kinds when it's needed
|
933
|
+
* {I18n::Inflector::API} has an instance variable @idb that contains database of inflections indexed by locale
|
934
|
+
* {I18n::Inflector::API_Strict} has an instance variable @idb that contains database of inflections indexed by locale
|
935
|
+
* Translation databases are kind of {I18n::Inflector::InflectionData} and {I18n::Inflector::InflectionData_Strict}
|
936
|
+
* When initializing translations a method from {I18n::Backend::Simple} (altered by {I18n::Backend::Inflector})
|
937
|
+
takes the loaded data, processes their <tt>i18n.inflections</tt> scope for each locale and creates database
|
938
|
+
objects which are kind of {I18n::Inflector::InflectionData} and {I18n::Inflector::InflectionData_Strict}. That
|
939
|
+
objects are then attached to instances of {I18n::Inflector::API} and {I18n::Inflector::API_Strict}.
|