i18n-inflector 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,964 @@
1
+ # encoding: utf-8
2
+ #
3
+ # Author:: Paweł Wilk (mailto:pw@gnu.org)
4
+ # Copyright:: (c) 2011 by Paweł Wilk
5
+ # License:: This program is licensed under the terms of {file:LGPL GNU Lesser General Public License} or {file:COPYING Ruby License}.
6
+ #
7
+ # This file contains {I18n::Inflector::API} class,
8
+ # which is public API for I18n Inflector.
9
+
10
+ module I18n
11
+ module Inflector
12
+
13
+ # Instance of this class, the inflector, is attached
14
+ # to I18n backend. This class contains common operations
15
+ # that can be performed on inflections. It can operate
16
+ # on both unnamed an named patterns (regular and strict kinds).
17
+ # This class is used by backend methods to interpolate
18
+ # strings and load inflections.
19
+ #
20
+ # It uses the databases containing instances of
21
+ # {I18n::Inflector::InflectionData} and {I18n::Inflector::InflectionData_Strict}
22
+ # that are stored in the Hashes and indexed by locale names.
23
+ #
24
+ # Note that strict kinds used to handle named patterns
25
+ # internally are stored in a different database than
26
+ # regular kinds. Most of the methods of this class are also
27
+ # aware of strict kinds and will call proper methods handling
28
+ # strict inflection data when the +@+ symbol is detected
29
+ # at the beginning of the given identifier of a kind.
30
+ #
31
+ # ==== Usage
32
+ # You can access the instance of this class attached to
33
+ # default I18n backend by calling:
34
+ # I18n.backend.inflector
35
+ # or in a short form:
36
+ # I18n.inflector
37
+ # In case of named patterns (strict kinds):
38
+ # I18n.inflector.strict
39
+ #
40
+ # @see I18n::Inflector::API_Strict The API_Strict class
41
+ # for accessing inflection data for named
42
+ # patterns (strict kinds).
43
+ # @see file:EXAMPLES The examples of real-life usage.
44
+ # @api public
45
+ class API < API_Strict
46
+
47
+ # Options controlling the engine.
48
+ #
49
+ # @api public
50
+ # @return [I18n::Inflector::InflectionOptions] the set of options
51
+ # controlling inflection engine
52
+ # @see I18n::Inflector::InflectionOptions#raises
53
+ # @see I18n::Inflector::InflectionOptions#unknown_defaults
54
+ # @see I18n::Inflector::InflectionOptions#excluded_defaults
55
+ # @see I18n::Inflector::InflectionOptions#aliased_patterns
56
+ # @example Usage of +options+:
57
+ # # globally set raises flag
58
+ # I18n.inflector.options.raises = true
59
+ #
60
+ # # globally set raises flag (the same meaning as the example above)
61
+ # I18n.backend.inflector.options.raises = true
62
+ #
63
+ # # set raises flag just for this translation
64
+ # I18n.translate('welcome', :inflector_raises => true)
65
+ attr_reader :options
66
+
67
+ # @private
68
+ def strict
69
+ @strict ||= I18n::Inflector::API_Strict.new(@idb_strict, @options)
70
+ end
71
+
72
+ # Initilizes the inflector by creating internal databases
73
+ # used for storing inflection data and options.
74
+ #
75
+ # @api public
76
+ def initialize
77
+ super(nil, nil)
78
+ @idb_strict = {}
79
+ end
80
+
81
+ # Creates a database for the specified locale.
82
+ #
83
+ # @api public
84
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
85
+ # @param [Symbol] locale the locale for which the inflections database is to be created
86
+ # @return [I18n::Inflector::InflectionData] the new object for keeping inflection data
87
+ def new_database(locale)
88
+ locale = prep_locale(locale)
89
+ @idb[locale] = I18n::Inflector::InflectionData.new(locale)
90
+ end
91
+
92
+ # Creates internal databases (regular and strict) for the specified locale.
93
+ #
94
+ # @api public
95
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
96
+ # @param [Symbol] locale the locale for which the inflections databases are to be created
97
+ # @return [Array<I18n::Inflector::InflectionData,I18n::Inflector::InflectionData_Strict>] the
98
+ # array of objects for keeping inflection data
99
+ def new_databases(locale)
100
+ normal = new_databases(locale)
101
+ strict = strict.new_database(locale)
102
+ [normal, strict]
103
+ end
104
+
105
+ # Attaches instance of {I18n::Inflector::InflectionData} and
106
+ # optionally {I18n::Inflector::InflectionData_Strict}
107
+ # to the inflector.
108
+ #
109
+ # @api public
110
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
111
+ # @note It doesn't create a copy of inflection data, but registers the given object(s).
112
+ # @return [I18n::Inflector::InflectionData,Array,nil]
113
+ # @overload add_database(db)
114
+ # @param [I18n::Inflector::InflectionData] db inflection data to add
115
+ # @return [I18n::Inflector::InflectionData,nil] the given object or +nil+
116
+ # if something went wrong (e.g. +nil+ was given as an argument)
117
+ # @overload add_database(db, db_strict)
118
+ # @note An array is returned and databases are
119
+ # used only if both databases are successfully attached. References to
120
+ # both databases will be unset if there would be a problem with attaching
121
+ # any of them.
122
+ # @param [I18n::Inflector::InflectionData] db inflection data to add
123
+ # @param [I18n::Inflector::InflectionData_Strict] db_strict strict inflection data to add
124
+ # @return [Array<I18n::Inflector::InflectionData,I18n::Inflector::InflectionData_Strict>,nil] the
125
+ # array of the given objects or +nil+ if something went wrong (e.g. +nil+ was
126
+ # given as the first argument)
127
+ def add_database(db, db_strict=nil)
128
+ r = super(db)
129
+ return r if (r.nil? || db_strict.nil?)
130
+ r_strict = strict.add_database(db_strict)
131
+ if r_strict.nil?
132
+ delete_database(db.locale)
133
+ return nil
134
+ end
135
+ [r, r_strict]
136
+ end
137
+ alias_method :add_databases, :add_database
138
+
139
+ # Deletes the internal databases for the specified locale.
140
+ #
141
+ # @api public
142
+ # @note It detaches the databases from {I18n::Inflector::API} instance.
143
+ # Other objects referring to them may still use it.
144
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
145
+ # @param [Symbol] locale the locale for which the inflections database is to be deleted.
146
+ # @return [void]
147
+ def delete_databases(locale)
148
+ delete_database(locale)
149
+ strict.delete_database(locale)
150
+ end
151
+
152
+ # Checks if the given locale was configured to support inflection.
153
+ #
154
+ # @api public
155
+ # @note That method uses information from regular and strict kinds.
156
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
157
+ # @return [Boolean] +true+ if a locale supports inflection
158
+ # @overload inflected_locale?(locale)
159
+ # Checks if the given locale was configured to support inflection.
160
+ # @param [Symbol] locale the locale to test
161
+ # @return [Boolean] +true+ if the given locale supports inflection
162
+ # @overload inflected_locale?
163
+ # Checks if the current locale was configured to support inflection.
164
+ # @return [Boolean] +true+ if the current locale supports inflection
165
+ def inflected_locale?(locale=nil)
166
+ super || strict.inflected_locale?(locale)
167
+ end
168
+ alias_method :locale?, :inflected_locale?
169
+ alias_method :locale_supported?, :inflected_locale?
170
+
171
+ # Gets locales which have configured inflection support.
172
+ #
173
+ # @api public
174
+ # @note That method uses information from regular and strict kinds.
175
+ # @return [Array<Symbol>] the array containing locales that support inflection
176
+ #
177
+ # @overload inflected_locales
178
+ # Gets locales which have configured inflection support.
179
+ # @return [Array<Symbol>] the array containing locales that support inflection
180
+ # @overload inflected_locales(kind)
181
+ # Gets locales which have configured inflection support for the given +kind+.
182
+ # @param [Symbol] kind the identifier of a kind
183
+ # @return [Array<Symbol>] the array containing locales that support inflection
184
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
185
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#inflected_locales})
186
+ def inflected_locales(kind=nil)
187
+ if kind.to_s[0..0] == NAMED_MARKER
188
+ strict.inflected_locales(kind.to_s[1..-1])
189
+ else
190
+ (super + strict.inflected_locales(kind)).uniq
191
+ end
192
+ end
193
+
194
+ # Tests if a kind exists.
195
+ #
196
+ # @api public
197
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
198
+ # @return [Boolean] +true+ if the given +kind+ exists, +false+ otherwise
199
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
200
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#has_kind?})
201
+ # @overload has_kind?(kind)
202
+ # Tests if a regular kind exists for the current locale.
203
+ # @param [Symbol] kind the identifier of a kind
204
+ # @return [Boolean] +true+ if the given +kind+ exists for the current
205
+ # locale, +false+ otherwise
206
+ # @overload has_kind?(kind, locale)
207
+ # Tests if a regular kind exists for the given +locale+.
208
+ # @param [Symbol,String] kind the identifier of a kind
209
+ # @param [Symbol] locale the locale identifier
210
+ # @return [Boolean] +true+ if the given +kind+ exists, +false+ otherwise
211
+ def has_kind?(kind, locale=nil)
212
+ if kind.to_s[0..0] == NAMED_MARKER
213
+ return strict.has_kind?(kind.to_s[1..-1], locale)
214
+ end
215
+ super
216
+ end
217
+
218
+ # Reads default token for the given +kind+.
219
+ #
220
+ # @api public
221
+ # @return [Symbol,nil] the default token or +nil+
222
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
223
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
224
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#default_token})
225
+ # @overload default_token(kind)
226
+ # This method reads default token for the given +kind+ and the current locale.
227
+ # @param [Symbol,String] kind the kind of tokens
228
+ # @return [Symbol,nil] the default token or +nil+ if
229
+ # there is no default token
230
+ # @overload default_token(kind, locale)
231
+ # This method reads default token for the given +kind+ and the given +locale+.
232
+ # @param [Symbol,String] kind the kind of tokens
233
+ # @param [Symbol] locale the locale to use
234
+ # @return [Symbol,nil] the default token or +nil+ if
235
+ # there is no default token
236
+ def default_token(kind, locale=nil)
237
+ return nil if (kind.nil? || kind.to_s.empty?)
238
+ if kind.to_s[0..0] == NAMED_MARKER
239
+ return strict.default_token(kind.to_s[1..-1], locale)
240
+ end
241
+ super
242
+ end
243
+
244
+ # Checks if the given +token+ is an alias.
245
+ #
246
+ # @api public
247
+ # @note By default it uses regular kinds database, not strict kinds.
248
+ # @return [Boolean] +true+ if the given +token+ is an alias, +false+ otherwise
249
+ # @raise [I18n::InvalidLocale] if the given +locale+ is invalid
250
+ # @raise [ArgumentError] if the count of arguments is invalid
251
+ # @overload has_alias?(token)
252
+ # Uses current locale to check if the given +token+ is an alias.
253
+ # @param [Symbol,String] token name of the checked token
254
+ # @return [Boolean] +true+ if the given +token+ is an alias, +false+ otherwise
255
+ # @overload has_alias?(token, locale)
256
+ # Uses the given +locale+ to check if the given +token+ is an alias.
257
+ # @param [Symbol,String] token name of the checked token
258
+ # @param [Symbol] locale the locale to use
259
+ # @return [Boolean] +true+ if the given +token+ is an alias, +false+ otherwise
260
+ # @overload has_alias?(token, kind, locale)
261
+ # Uses the given +locale+ and +kind+ to check if the given +token+ is an alias.
262
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
263
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#has_alias?})
264
+ # @param [Symbol,String] token name of the checked token
265
+ # @param [Symbol,String] kind the kind used to narrow the check
266
+ # @param [Symbol] locale the locale to use
267
+ # @return [Boolean] +true+ if the given +token+ is an alias, +false+ otherwise
268
+ # @overload has_alias?(token, strict_kind)
269
+ # Uses the current locale and the given +strict_kind+ (which name must begin with
270
+ # the +@+ symbol) to check if the given +token+ is an alias.
271
+ # @note It calls {I18n::Inflector::API_Strict#has_alias?} on strict kinds data.
272
+ # @param [Symbol,String] token name of the checked token
273
+ # @param [Symbol,String] strict_kind the kind of the given alias
274
+ # @return [Boolean] +true+ if the given +token+ is an alias, +false+ otherwise
275
+ def has_alias?(*args)
276
+ token, kind, locale = tkl_args(args)
277
+ return false if (token.nil? || token.to_s.empty?)
278
+ unless kind.nil?
279
+ kind = kind.to_s
280
+ reutrn false if kind.empty?
281
+ if kind[0..0] == NAMED_MARKER
282
+ return strict.has_alias?(token, kind[1..-1], locale)
283
+ end
284
+ kind = kind.to_sym
285
+ end
286
+ data_safe(locale).has_alias?(token.to_sym, kind)
287
+ end
288
+ alias_method :token_has_alias?, :has_alias?
289
+
290
+ # Checks if the given +token+ is a true token (not alias).
291
+ #
292
+ # @api public
293
+ # @note By default it uses regular kinds database, not strict kinds.
294
+ # @return [Boolean] +true+ if the given +token+ is a true token, +false+ otherwise
295
+ # @raise [I18n::InvalidLocale] if the given +locale+ is invalid
296
+ # @raise [ArgumentError] if the count of arguments is invalid
297
+ # @overload has_true_token?(token)
298
+ # Uses current locale to check if the given +token+ is a true token.
299
+ # @param [Symbol,String] token name of the checked token
300
+ # @return [Boolean] +true+ if the given +token+ is a true token, +false+ otherwise
301
+ # @overload has_true_token?(token, locale)
302
+ # Uses the given +locale+ to check if the given +token+ is a true token.
303
+ # @param [Symbol,String] token name of the checked token
304
+ # @param [Symbol] locale the locale to use
305
+ # @return [Boolean] +true+ if the given +token+ is a true token, +false+ otherwise
306
+ # @overload has_true_token?(token, kind, locale)
307
+ # Uses the given +locale+ and +kind+ to check if the given +token+ is a true token.
308
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
309
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#has_true_token?})
310
+ # @param [Symbol,String] token name of the checked token
311
+ # @param [Symbol,String] kind the kind used to narrow the check
312
+ # @param [Symbol] locale the locale to use
313
+ # @return [Boolean] +true+ if the given +token+ is a true token, +false+ otherwise
314
+ # @overload has_true_token?(token, strict_kind)
315
+ # Uses the current locale and the given +strict_kind+ (which name must begin with
316
+ # the +@+ symbol) to check if the given +token+ is a true token.
317
+ # @note It calls {I18n::Inflector::API_Strict#has_true_token?} on strict kinds data.
318
+ # @param [Symbol,String] token name of the checked token
319
+ # @param [Symbol,String] strict_kind the kind of the given token
320
+ # @return [Boolean] +true+ if the given +token+ is a true token, +false+ otherwise
321
+ def has_true_token?(*args)
322
+ token, kind, locale = tkl_args(args)
323
+ return false if (token.nil? || token.to_s.empty?)
324
+ unless kind.nil?
325
+ kind = kind.to_s
326
+ return false if kind.empty?
327
+ if kind[0..0] == NAMED_MARKER
328
+ return strict.has_true_token?(token, kind[1..-1], locale)
329
+ end
330
+ kind = kind.to_sym
331
+ end
332
+ data_safe(locale).has_true_token?(token.to_sym, kind)
333
+ end
334
+ alias_method :token_has_true?, :has_true_token?
335
+
336
+ # Checks if the given +token+ exists. It may be an alias or a true token.
337
+ #
338
+ # @api public
339
+ # @note By default it uses regular kinds database, not strict kinds.
340
+ # @return [Boolean] +true+ if the given +token+ exists, +false+ otherwise
341
+ # @raise [I18n::InvalidLocale] if the given +locale+ is invalid
342
+ # @raise [ArgumentError] if the count of arguments is invalid
343
+ # @overload has_token?(token)
344
+ # Uses current locale to check if the given +token+ is a token.
345
+ # @param [Symbol,String] token name of the checked token
346
+ # @return [Boolean] +true+ if the given +token+ exists, +false+ otherwise
347
+ # @overload has_token?(token, locale)
348
+ # Uses the given +locale+ to check if the given +token+ exists.
349
+ # @param [Symbol,String] token name of the checked token
350
+ # @param [Symbol] locale the locale to use
351
+ # @return [Boolean] +true+ if the given +token+ exists, +false+ otherwise
352
+ # @overload has_token?(token, kind, locale)
353
+ # Uses the given +locale+ and +kind+ to check if the given +token+ exists.
354
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
355
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#has_token?})
356
+ # @param [Symbol,String] token name of the checked token
357
+ # @param [Symbol,String] kind the kind used to narrow the check
358
+ # @param [Symbol] locale the locale to use
359
+ # @return [Boolean] +true+ if the given +token+ exists, +false+ otherwise
360
+ # @overload has_token?(token, strict_kind)
361
+ # Uses the current locale and the given +strict_kind+ (which name must begin with
362
+ # the +@+ symbol) to check if the given +token+ exists.
363
+ # @note It calls {I18n::Inflector::API_Strict#has_token?} on strict kinds data.
364
+ # @param [Symbol,String] token name of the checked token
365
+ # @param [Symbol,String] strict_kind the kind of the given token
366
+ # @return [Boolean] +true+ if the given +token+ exists, +false+ otherwise
367
+ def has_token?(*args)
368
+ token, kind, locale = tkl_args(args)
369
+ return false if (token.nil? || token.to_s.empty?)
370
+ unless kind.nil?
371
+ kind = kind.to_s
372
+ return false if kind.empty?
373
+ if kind[0..0] == NAMED_MARKER
374
+ return strict.has_token?(token, kind[1..-1], locale)
375
+ end
376
+ kind = kind.to_sym
377
+ end
378
+ data_safe(locale).has_token?(token.to_sym, kind)
379
+ end
380
+ alias_method :token_exists?, :has_token?
381
+
382
+ # Gets true token for the given +token+. If the token
383
+ # is an alias it will be resolved
384
+ # and a true token (target) will be returned.
385
+ # @note By default it uses regular kinds database, not strict kinds.
386
+ # @api public
387
+ # @return [Symbol,nil] the true token or +nil+
388
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
389
+ # @overload true_token(token)
390
+ # Uses current locale to get a real token for the given +token+.
391
+ # @param [Symbol,String] token name of the checked token
392
+ # @return [Symbol,nil] the true token or +nil+
393
+ # @overload true_token(token, locale)
394
+ # Uses the given +locale+ to get a real token for the given +token+.
395
+ # If the token is an alias it will be resolved
396
+ # and a true token (target) will be returned.
397
+ # @param [Symbol,String] token name of the checked token
398
+ # @param [Symbol] locale the locale to use
399
+ # @return [Symbol,nil] the true token or +nil+
400
+ # @overload true_token(token, kind, locale)
401
+ # Uses the given +locale+ and +kind+ to get a real token for the given +token+.
402
+ # If the token is an alias it will be resolved
403
+ # and a true token (target) will be returned.
404
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
405
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#true_token})
406
+ # @param [Symbol,String] token name of the checked token
407
+ # @param [Symbol,String] kind the kind of the given token
408
+ # @param [Symbol] locale the locale to use
409
+ # @return [Symbol,nil] the true token or +nil+
410
+ # @overload true_token(token, strict_kind)
411
+ # Uses the current locale and the given +strict_kind+ (which name must begin with
412
+ # the +@+ symbol) to get a real token for the given +token+.
413
+ # @note It calls {I18n::Inflector::API_Strict#true_token} on strict kinds data.
414
+ # @param [Symbol,String] token name of the checked token
415
+ # @param [Symbol,String] strict_kind the kind of the given token
416
+ # @return [Symbol,nil] the true token
417
+ def true_token(*args)
418
+ token, kind, locale = tkl_args(args)
419
+ return nil if (token.nil? || token.to_s.empty?)
420
+ unless kind.nil?
421
+ kind = kind.to_s
422
+ return nil if kind.empty?
423
+ if kind[0..0] == NAMED_MARKER
424
+ return strict.true_token(token, kind[1..-1], locale)
425
+ end
426
+ kind = kind.to_sym
427
+ end
428
+ data_safe(locale).get_true_token(token.to_sym, kind)
429
+ end
430
+ alias_method :resolve_alias, :true_token
431
+
432
+ # Gets a kind for the given +token+ (which may be an alias).
433
+ #
434
+ # @api public
435
+ # @note By default it uses regular kinds database, not strict kinds.
436
+ # @return [Symbol,nil] the kind of the given +token+ or +nil+
437
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
438
+ # @overload kind(token)
439
+ # Uses the current locale to get a kind of the given +token+ (which may be an alias).
440
+ # @param [Symbol,String] token name of the token or alias
441
+ # @return [Symbol,nil] the kind of the given +token+
442
+ # @overload kind(token, locale)
443
+ # Uses the given +locale+ to get a kind of the given +token+ (which may be an alias).
444
+ # @param [Symbol,String] token name of the token or alias
445
+ # @param [Symbol] locale the locale to use
446
+ # @return [Symbol,nil] the kind of the given +token+
447
+ # @overload kind(token, kind, locale)
448
+ # Uses the given +locale+ to get a kind of the given +token+ (which may be an alias).
449
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
450
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#kind})
451
+ # @param [Symbol,String] token name of the token or alias
452
+ # @param [Symbol,String] kind the kind name to narrow the search
453
+ # @param [Symbol] locale the locale to use
454
+ # @return [Symbol,nil] the kind of the given +token+
455
+ # @overload kind(token, strict_kind)
456
+ # Uses the current locale and the given +strict_kind+ (which name must begin with
457
+ # the +@+ symbol) to get a kind of the given +token+ (which may be an alias).
458
+ # @note It calls {I18n::Inflector::API_Strict#kind} on strict kinds data.
459
+ # @param [Symbol,String] token name of the token or alias
460
+ # @param [Symbol,String] kind the kind of the given token
461
+ # @return [Symbol,nil] the kind of the given +token+
462
+ def kind(*args)
463
+ token, kind, locale = tkl_args(args)
464
+ return nil if (token.nil? || token.to_s.empty?)
465
+ unless kind.nil?
466
+ kind = kind.to_s
467
+ return nil if kind.empty?
468
+ if kind[0..0] == NAMED_MARKER
469
+ return strict.kind(token, kind[1..-1], locale)
470
+ end
471
+ kind = kind.to_sym
472
+ end
473
+ data_safe(locale).get_kind(token.to_sym, kind)
474
+ end
475
+
476
+ # Gets available inflection tokens and their descriptions.
477
+ #
478
+ # @api public
479
+ # @note By default it uses regular kinds database, not strict kinds.
480
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
481
+ # @return [Hash] the hash containing available inflection tokens and descriptions
482
+ # @note You cannot deduce where aliases are pointing to, since the information
483
+ # about a target is replaced by the description. To get targets use the
484
+ # {#raw_tokens} method. To simply list aliases and their targets use
485
+ # the {#aliases} method.
486
+ # @overload tokens
487
+ # Gets available inflection tokens and their descriptions.
488
+ # @return [Hash] the hash containing available inflection tokens as keys
489
+ # and their descriptions as values, including aliases,
490
+ # for all kinds.
491
+ # @overload tokens(kind)
492
+ # Gets available inflection tokens and their descriptions for some +kind+.
493
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
494
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#tokens})
495
+ # @param [Symbol,String] kind the kind of inflection tokens to be returned
496
+ # @return [Hash] the hash containing available inflection tokens as keys
497
+ # and their descriptions as values, including aliases, for current locale.
498
+ # @overload tokens(kind, locale)
499
+ # Gets available inflection tokens and their descriptions for some +kind+ and +locale+.
500
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
501
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#tokens})
502
+ # @param [Symbol,String] kind the kind of inflection tokens to be returned
503
+ # @param [Symbol] locale the locale to use
504
+ # @return [Hash] the hash containing available inflection tokens as keys
505
+ # and their descriptions as values, including aliases, for current locale
506
+ def tokens(kind=nil, locale=nil)
507
+ unless kind.nil?
508
+ kind = kind.to_s
509
+ return {} if kind.empty?
510
+ if kind[0..0] == NAMED_MARKER
511
+ return strict.tokens(kind[1..-1], locale)
512
+ end
513
+ kind = kind.to_sym
514
+ end
515
+ data_safe(locale).get_tokens(kind)
516
+ end
517
+
518
+ # Gets available inflection tokens and their values.
519
+ #
520
+ # @api public
521
+ # @return [Hash] the hash containing available inflection tokens and descriptions (or alias pointers)
522
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
523
+ # @note You may deduce whether the returned values are aliases or true tokens
524
+ # by testing if a value is a type of Symbol or String.
525
+ # @overload tokens_raw
526
+ # Gets available inflection tokens and their values for regular kinds.
527
+ # @return [Hash] the hash containing available inflection tokens as keys
528
+ # and their descriptions as values. In case of aliases the returned
529
+ # values are Symbols
530
+ # @overload tokens_raw(kind)
531
+ # Gets available inflection tokens and their values for the given +kind+.
532
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
533
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#tokens_raw})
534
+ # @param [Symbol,String] kind the kind of inflection tokens to be returned
535
+ # @return [Hash] the hash containing available inflection tokens as keys
536
+ # and their descriptions as values. In case of aliases the returned
537
+ # values are Symbols
538
+ # @overload tokens_raw(kind, locale)
539
+ # Gets available inflection tokens and their values for the given +kind+ and +locale+.
540
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
541
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#tokens_raw})
542
+ # @param [Symbol,String] kind the kind of inflection tokens to be returned
543
+ # @param [Symbol] locale the locale to use
544
+ # @return [Hash] the hash containing available inflection tokens as keys
545
+ # and their descriptions as values. In case of aliases the returned
546
+ # values are Symbols
547
+ def tokens_raw(kind=nil, locale=nil)
548
+ unless kind.nil?
549
+ kind = kind.to_s
550
+ return {} if kind.empty?
551
+ if kind[0..0] == NAMED_MARKER
552
+ return strict.tokens_raw(kind[1..-1], locale)
553
+ end
554
+ kind = kind.to_sym
555
+ end
556
+ data_safe(locale).get_raw_tokens(kind)
557
+ end
558
+ alias_method :raw_tokens, :tokens_raw
559
+
560
+ # Gets true inflection tokens and their values.
561
+ #
562
+ # @api public
563
+ # @return [Hash] the hash containing available inflection tokens and descriptions
564
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
565
+ # @note It returns only true tokens, not aliases.
566
+ # @overload tokens_true
567
+ # Gets true inflection tokens and their values for regular kinds.
568
+ # @return [Hash] the hash containing available inflection tokens as keys
569
+ # and their descriptions as values
570
+ # @overload tokens_true(kind)
571
+ # Gets true inflection tokens and their values for the given +kind+.
572
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
573
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#tokens_true})
574
+ # @param [Symbol,String] kind the kind of inflection tokens to be returned
575
+ # @return [Hash] the hash containing available inflection tokens as keys
576
+ # and their descriptions as values
577
+ # @overload tokens_true(kind, locale)
578
+ # Gets true inflection tokens and their values for the given +kind+ and +value+.
579
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
580
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#tokens_true})
581
+ # @param [Symbol,String] kind the kind of inflection tokens to be returned
582
+ # @param [Symbol] locale the locale to use
583
+ # @return [Hash] the hash containing available inflection tokens as keys
584
+ # and their descriptions as values
585
+ def tokens_true(kind=nil, locale=nil)
586
+ unless kind.nil?
587
+ kind = kind.to_s
588
+ return {} if kind.empty?
589
+ if kind[0..0] == NAMED_MARKER
590
+ return strict.tokens_true(kind[1..-1], locale)
591
+ end
592
+ kind = kind.to_sym
593
+ end
594
+ data_safe(locale).get_true_tokens(kind)
595
+ end
596
+ alias_method :true_tokens, :tokens_true
597
+
598
+ # Gets inflection aliases and their pointers.
599
+ #
600
+ # @api public
601
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
602
+ # @return [Hash] the Hash containing available inflection aliases (<tt>alias => target</tt>)
603
+ # @overload aliases
604
+ # Gets inflection aliases and their pointers for regular kinds.
605
+ # @return [Hash] the Hash containing available inflection aliases
606
+ # @overload aliases(kind)
607
+ # Gets inflection aliases and their pointers for the given +kind+.
608
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
609
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#aliases})
610
+ # @param [Symbol,String] kind the kind of aliases to get
611
+ # @return [Hash] the Hash containing available inflection aliases
612
+ # @overload aliases(kind, locale)
613
+ # Gets inflection aliases and their pointers for the given +kind+ and +locale+.
614
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
615
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#aliases})
616
+ # @param [Symbol,String] kind the kind of aliases to get
617
+ # @param [Symbol] locale the locale to use
618
+ # @return [Hash] the Hash containing available inflection aliases
619
+ def aliases(kind=nil, locale=nil)
620
+ unless kind.nil?
621
+ kind = kind.to_s
622
+ return nil if kind.empty?
623
+ if kind[0..0] == NAMED_MARKER
624
+ return strict.aliases(kind[1..-1], locale)
625
+ end
626
+ kind = kind.to_sym
627
+ end
628
+ data_safe(locale).get_aliases(kind)
629
+ end
630
+
631
+ # Gets the description of the given inflection token.
632
+ #
633
+ # @api public
634
+ # @note If the given +token+ is really an alias it
635
+ # returns the description of the true token that
636
+ # it points to. By default it uses regular kinds database,
637
+ # not strict kinds.
638
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
639
+ # @return [String,nil] the descriptive string or +nil+
640
+ # @overload token_description(token)
641
+ # Uses current locale to get description of the given inflection +token+.
642
+ # @param [Symbol] token the identifier of a token
643
+ # @return [String,nil] the descriptive string or +nil+ if something
644
+ # went wrong (e.g. token was not found)
645
+ # @overload token_description(token, locale)
646
+ # Uses the given +locale+ to get description of the given inflection +token+.
647
+ # @param [Symbol,String] token the identifier of a token
648
+ # @param [Symbol] locale the locale to use
649
+ # @return [String,nil] the descriptive string or +nil+ if something
650
+ # went wrong (e.g. token was not found)
651
+ # @overload token_description(token, kind, locale)
652
+ # Uses the given +locale+ and +kind+ to get description of the given inflection +token+.
653
+ # @note If +kind+ begins with the +@+ symbol then the variant of this method
654
+ # operating on strict kinds will be called ({I18n::Inflector::API_Strict#token_description})
655
+ # @param [Symbol,String] token the identifier of a token
656
+ # @param [Symbol,String] kind the kind to narrow the results
657
+ # @param [Symbol] locale the locale to use
658
+ # @return [String,nil] the descriptive string or +nil+ if something
659
+ # went wrong (e.g. token was not found or +kind+ mismatched)
660
+ # @overload token_description(token, strict_kind)
661
+ # Uses the default locale and the given +kind+ (which name must begin with
662
+ # the +@+ symbol) to get description of the given inflection +token+.
663
+ # @note It calls {I18n::Inflector::API_Strict#token_description} on strict kinds data.
664
+ # @param [Symbol,String] token the identifier of a token
665
+ # @param [Symbol,String] strict_kind the kind of a token
666
+ # @param [Symbol] locale the locale to use
667
+ # @return [String,nil] the descriptive string or +nil+ if something
668
+ # went wrong (e.g. token was not found or +kind+ mismatched)
669
+ def token_description(*args)
670
+ token, kind, locale = tkl_args(args)
671
+ return nil if (token.nil? || token.to_s.empty?)
672
+ unless kind.nil?
673
+ kind = kind.to_s
674
+ return nil if kind.empty?
675
+ if kind[0..0] == NAMED_MARKER
676
+ return strict.token_description(token, kind[1..-1], locale)
677
+ end
678
+ kind = kind.to_sym
679
+ end
680
+ data_safe(locale).get_description(token.to_sym, kind)
681
+ end
682
+
683
+ # Interpolates inflection values in the given +string+
684
+ # using kinds given in +options+ and a matching tokens.
685
+ #
686
+ # @param [String] string the translation string
687
+ # containing patterns to interpolate
688
+ # @param [String,Symbol] locale the locale identifier
689
+ # @param [Hash] options the options
690
+ # @option options [Boolean] :inflector_excluded_defaults (false) local switch
691
+ # that overrides global setting (see: {I18n::Inflector::InflectionOptions#excluded_defaults})
692
+ # @option options [Boolean] :inflector_unknown_defaults (true) local switch
693
+ # that overrides global setting (see: {I18n::Inflector::InflectionOptions#unknown_defaults})
694
+ # @option options [Boolean] :inflector_raises (false) local switch
695
+ # that overrides global setting (see: {I18n::Inflector::InflectionOptions#raises})
696
+ # @option options [Boolean] :inflector_aliased_patterns (false) local switch
697
+ # that overrides global setting (see: {I18n::Inflector::InflectionOptions#aliased_patterns})
698
+ # @return [String] the string with interpolated patterns
699
+ def interpolate(string, locale, options = {})
700
+ used_kinds = options.except(*INFLECTOR_RESERVED_KEYS)
701
+ sw, op = @options, options
702
+ raises = (s=op.delete :inflector_raises).nil? ? sw.raises : s
703
+ aliased_patterns = (s=op.delete :inflector_aliased_patterns).nil? ? sw.aliased_patterns : s
704
+ unknown_defaults = (s=op.delete :inflector_unknown_defaults).nil? ? sw.unknown_defaults : s
705
+ excluded_defaults = (s=op.delete :inflector_excluded_defaults).nil? ? sw.excluded_defaults : s
706
+
707
+ idb = @idb[locale]
708
+ idb_strict = @idb_strict[locale]
709
+
710
+ string.gsub(PATTERN) do
711
+ pattern_fix = $1
712
+ strict_kind = $2
713
+ pattern_content = $3
714
+ ext_pattern = $&
715
+ ext_value = nil
716
+ ext_freetext = ''
717
+ found = false
718
+ parsed_default_v= nil
719
+
720
+ # leave escaped pattern as-is
721
+ unless pattern_fix.empty?
722
+ ext_pattern = ext_pattern[1..-1]
723
+ next ext_pattern if ESCAPES[pattern_fix]
724
+ end
725
+
726
+ # set parsed kind if strict kind is given (named pattern is parsed)
727
+ if strict_kind.empty?
728
+ parsed_symbol = nil
729
+ strict_kind = nil
730
+ parsed_kind = nil
731
+ default_token = nil
732
+ subdb = idb
733
+ else
734
+ parsed_symbol = (NAMED_MARKER + strict_kind).to_sym
735
+ strict_kind = strict_kind.to_sym
736
+ parsed_kind = strict_kind
737
+ subdb = idb_strict
738
+ default_token = subdb.get_default_token(parsed_kind)
739
+ end
740
+
741
+ # process pattern content's
742
+ pattern_content.scan(TOKENS) do
743
+ ext_token = $1.to_s
744
+ ext_value = $2.to_s
745
+ ext_freetext = $3.to_s
746
+ tokens = Hash.new(false)
747
+ negatives = Hash.new(false)
748
+ kind = nil
749
+ option = nil
750
+
751
+ # token not found?
752
+ if ext_token.empty?
753
+ # free text not found too? that should never happend.
754
+ if ext_freetext.empty?
755
+ raise I18n::InvalidInflectionToken.new(ext_pattern, ext_token) if raises
756
+ end
757
+ next
758
+ end
759
+
760
+ # split tokens if comma is present and put into fast list
761
+ ext_token.split(OPERATOR_MULTI).each do |t|
762
+ # token name corrupted
763
+ if t.empty?
764
+ raise I18n::InvalidInflectionToken.new(ext_pattern, t) if raises
765
+ next
766
+ end
767
+
768
+ # mark negative-matching tokens and put them to negatives fast list
769
+ if t[0..0] == OPERATOR_NOT
770
+ t = t[1..-1]
771
+ if t.empty?
772
+ raise I18n::InvalidInflectionToken.new(ext_pattern, t) if raises
773
+ next
774
+ end
775
+ t = t.to_sym
776
+ t = subdb.get_true_token(t, strict_kind) if aliased_patterns
777
+ negatives[t] = true
778
+ end
779
+
780
+ t = t.to_sym
781
+ t = subdb.get_true_token(t, strict_kind) if aliased_patterns
782
+
783
+ # get kind for that token
784
+ kind = subdb.get_kind(t, strict_kind)
785
+ if kind.nil?
786
+ raise I18n::InvalidInflectionToken.new(ext_pattern, t) if raises
787
+ next
788
+ end
789
+
790
+ # set processed kind after matching first token in a pattern
791
+ if parsed_kind.nil?
792
+ parsed_kind = kind
793
+ parsed_symbol = kind.to_sym
794
+ default_token = subdb.get_default_token(parsed_kind)
795
+ elsif parsed_kind != kind
796
+ # tokens of different kinds in one regular (not named) pattern are prohibited
797
+ if raises
798
+ raise I18n::MisplacedInflectionToken.new(ext_pattern, t, parsed_symbol)
799
+ end
800
+ next
801
+ end
802
+
803
+ # use that token
804
+ tokens[t] = true unless negatives[t]
805
+ end
806
+
807
+ # self-explanatory
808
+ if (tokens.empty? && negatives.empty?)
809
+ raise I18n::InvalidInflectionToken.new(ext_pattern, ext_token) if raises
810
+ end
811
+
812
+ # try @-style option for strict kind, fallback to regular if not found
813
+ # and memorize option name for error reporting
814
+ oname = !strict_kind.nil? && options.has_key?(parsed_symbol) ?
815
+ parsed_symbol : (options.has_key?(kind) ? kind : nil)
816
+
817
+ # Get option if possible and memorize for error reporting;
818
+ # fallback to default token if option still not found
819
+ if oname.nil?
820
+ option = default_token
821
+ orig_option = nil
822
+ else
823
+ option = options[oname]
824
+ orig_option = option
825
+ end
826
+
827
+ if (option.nil? || option.to_s.empty?)
828
+ # if option is given but is unknown, empty or nil
829
+ # then use default option for a kind if unknown_defaults is switched on
830
+ option = unknown_defaults ? default_token : nil
831
+ else
832
+ # validate option and if it's unknown try in aliases
833
+ option = subdb.get_true_token(option.to_sym, strict_kind)
834
+
835
+ # if still nothing then fall back to default value
836
+ # for a kind if unknown_defaults switch is on
837
+ if option.nil?
838
+ option = unknown_defaults ? default_token : nil
839
+ end
840
+ end
841
+
842
+ # if the option is still unknown or bad
843
+ # raise an exception
844
+ if option.nil?
845
+ if raises
846
+ if oname.nil?
847
+ ex = InflectionOptionNotFound
848
+ oname = parsed_symbol
849
+ orig_option = nil
850
+ else
851
+ ex = InflectionOptionIncorrect
852
+ end
853
+ raise ex.new(ext_pattern, oname, ext_token, orig_option)
854
+ end
855
+ next
856
+ end
857
+
858
+ # memorize default value for further processing
859
+ # outside this block if excluded_defaults switch is on
860
+ parsed_default_v = ext_value if (excluded_defaults && !default_token.nil?)
861
+
862
+ # throw the value if the given option matches one of the tokens from group
863
+ # or negatively matches one of the negated tokens
864
+ case negatives.count
865
+ when 0 then next unless tokens[option]
866
+ when 1 then next if negatives[option]
867
+ end
868
+
869
+ # skip further evaluation of the pattern
870
+ # since the right token has been found
871
+ found = true
872
+ break
873
+
874
+ end # single token (or a group) processing
875
+
876
+ result = nil
877
+
878
+ # return value of a token that matches option's value
879
+ # given for a kind or try to return a free text
880
+ # if it's present
881
+ if found
882
+ result = ext_value
883
+ elsif (excluded_defaults && !parsed_kind.nil?)
884
+ # if there is excluded_defaults switch turned on
885
+ # and a correct token was found in an inflection option but
886
+ # has not been found in a pattern then interpolate
887
+ # the pattern with a value picked for the default
888
+ # token for that kind if a default token was present
889
+ # in a pattern
890
+ kind = nil
891
+ token = options[parsed_kind]
892
+ kind = subdb.get_kind(token)
893
+ result = parsed_default_v unless kind.nil?
894
+ end
895
+
896
+ pattern_fix + (result || ext_freetext)
897
+
898
+ end # single pattern processing
899
+
900
+ end
901
+
902
+ protected
903
+
904
+ # @private
905
+ def data(locale=nil)
906
+ @idb[prep_locale(locale)]
907
+ end
908
+
909
+ # @private
910
+ def data_safe(locale=nil)
911
+ @idb[prep_locale(locale)] || I18n::Inflector::InflectionData.new(locale)
912
+ end
913
+
914
+ # This method is the internal helper that prepares arguments
915
+ # containing +token+, +kind+ and +locale+.
916
+ #
917
+ # @note This method leaves +kind+ as is when it's +nil+ or empty. It sets
918
+ # +token+ to +nil+ when it's empty.
919
+ # @raise [I18n::InvalidLocale] if there is no proper locale name
920
+ # @raise [ArgumentError] if the count of arguments is invalid
921
+ # @return [Array<Symbol,Symbol,Symbol>] the array containing
922
+ # cleaned and validated +token+, +kind+ and +locale+
923
+ # @overload tkl_args(token, kind, locale)
924
+ # Prepares arguments containing +token+, +kind+ and +locale+.
925
+ # @param [String,Hash] token the token
926
+ # @param [String,Hash] kind the inflection kind
927
+ # @param [String,Hash] locale the locale identifier
928
+ # @return [Array<Symbol,Symbol,Symbol>] the array containing
929
+ # cleaned and validated +token+, +kind+ and +locale+
930
+ # @overload tkl_args(token, locale)
931
+ # Prepares arguments containing +token+ and +locale+.
932
+ # @param [String,Hash] token the token
933
+ # @param [String,Hash] locale the locale identifier
934
+ # @return [Array<Symbol,Symbol,Symbol>] the array containing
935
+ # cleaned and validated +token+, +kind+ and +locale+
936
+ # @overload tkl_args(token)
937
+ # Prepares arguments containing +token+.
938
+ # @param [String,Hash] token the token
939
+ # @return [Array<Symbol,Symbol,Symbol>] the array containing
940
+ # cleaned and validated +token+ and the current locale
941
+ # @overload tkl_args(token, strict_kind)
942
+ # Prepares arguments containing +token+ and +strict_kind+.
943
+ # @param [String,Hash] token the token
944
+ # @param [String,Hash] strict_kind the strict kind identifier beginning with +@+ symbol
945
+ # @return [Array<Symbol,Symbol,Symbol>] the array containing
946
+ # cleaned and validated +token+, +strict_kind+ and the current locale
947
+ def tkl_args(args)
948
+ token, kind, locale = case args.count
949
+ when 1 then [args[0], nil, nil]
950
+ when 2 then args[1].to_s[0..0] == NAMED_MARKER ? [args[0], args[1], nil] : [args[0], nil, args[1]]
951
+ when 3 then args
952
+ else raise ArgumentError.new("wrong number of arguments: #{args.count} for (1..3)")
953
+ end
954
+ [token,kind,locale]
955
+ end
956
+
957
+ end # class API
958
+
959
+ # @abstract This is for backward compatibility with the old naming scheme.
960
+ class Core < API
961
+ end
962
+
963
+ end # module Inflector
964
+ end # module I18n