i18n-inflector 2.0.1 → 2.1.0

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