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