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/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}.