1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
|
# encoding: utf-8
#
# Author:: Paweł Wilk (mailto:pw@gnu.org)
# Copyright:: (c) 2011-2013 by by Paweł Wilk
# License:: This program is licensed under the terms of {file:LGPL-LICENSE GNU Lesser General Public License} or {file:COPYING Ruby License}.
#
# This file contains I18n::Backend::Inflector::Rails module,
# which extends ActionView::Helpers::TranslationHelper
# by adding the ability to interpolate patterns containing
# inflection tokens defined in translation data.
module I18n
# @abstract It is defined in {I18n Inflector library}[http://rubydoc.info/gems/i18n-inflector].
module Inflector
module Rails
# This module contains instance methods for ActionController.
module InstanceMethods
# This method calls the class method {I18n::Inflector::Rails::ClassMethods#i18n_inflector_kinds}
def i18n_inflector_kinds
self.class.i18n_inflector_kinds
end
# @private
def self.included(base)
base.helper_method(:i18n_inflector_kinds)
end
end # instance methods
# This module contains class methods for ActionController.
module ClassMethods
# This method reads the internal Hash +i18n_inflector_kinds+ containing registered
# inflection methods and the assigned kinds. It also reads any methods
# assignments that were defined earlier in the inheritance path and
# merges them with current results; the most current entries will
# override the entries defined before.
#
# @api public
# @return [Hash] the Hash containing assignments made by using {#inflection_method}
def i18n_inflector_kinds
prev = superclass.respond_to?(:i18n_inflector_kinds) ? superclass.i18n_inflector_kinds : {}
return @i18n_inflector_kinds.nil? ? prev : prev.merge(@i18n_inflector_kinds)
end
# This method allows to assign methods (typically attribute readers)
# to inflection kinds that are defined in translation files and
# supported by {I18n::Inflector} module. Methods registered like that
# will be tracked when {translate} is used and their returning values will be
# passed as inflection options along with assigned kinds. If the kind is not
# given then method assumes that the name of a kind is the same as the given
# name of a method.
#
# If the given kind begins with +@+ then strict kind is assumed. If there is
# no kind given but the given method name begins with +@+ character then
# also strict kind of the same name is assumed but method name is memorized
# without the leading symbol.
#
# Registering method for feeding an inflection option describing a strict
# kind might be good idea when using some regular kind of the same name,
# but note that regular kind inflection option is also tried by the
# translation method when strict kind is in use.
#
# In case of registering two methods of different
# names but assigned to kind and to a strict kind using the same base name,
# a named inflection pattern will first use an inflection option obtained
# from a method assigned to a strict kind. Note that it is impossible
# to use short formed +inflection_method+ calls to register a method for both
# strict and regular inflection kind, since the method names will be the
# same and the second call will overwrite the first one.
#
# @example Registering an inflection method for the kind gender visible in a whole application
# class ApplicationController < ActionController::Base
# inflection_method :gender
# […]
# end
#
# @example Registering an inflection method for the strict kind @gender visible in a whole application
# class ApplicationController < ActionController::Base
# inflection_method :@gender
# […]
# end
#
# @example Registering a custom-named inflection method for the kind gender
# class ApplicationController < ActionController::Base
# inflection_method :get_user_gender => :gender
# […]
# end
#
# @example Registering a custom-named inflection methods for the kinds gender and tense
# class ApplicationController < ActionController::Base
# inflection_method :get_user_gender => :gender, :get_tense => :tense
# […]
# end
#
# @example Registering inflection methods for the kinds gender and tense
# class ApplicationController < ActionController::Base
# inflection_method :gender, :tense
# […]
# end
#
# @example Registering inflection methods for the kind gender and the strict kind @tense
# class ApplicationController < ActionController::Base
# inflection_method :gender, :@tense
# […]
# end
#
# @example Registering a custom-named inflection methods for the kinds gender and @gender
# # in case of named patterns the method get_strict_gender and the
# # strict kind @gender will have priority; the regular kind gender
# # and its method would be used if there would be no strict variants
# class ApplicationController < ActionController::Base
# inflection_method :get_gender => :gender, :get_strict_gender => :@gender
# […]
# end
#
# @example Registering a method for the kind gender and the custom-named method for the kind @gender
# class ApplicationController < ActionController::Base
# inflection_method :gender, :get_strict_gender => :@gender
# […]
# end
#
# @example Registering a method for the kind gender visible in whole app and a variant for some controller
# class ApplicationController < ActionController::Base
# inflection_method :gender
# […]
# end
#
# # In this controller the method gender will be called
# # to obtain inflection option's value for the kind gender
# class UsersController < ApplicationController
# end
#
# # In this controller the method getit will be called
# # to obtain inflection option's value for the kind gender
# class OtherController < ApplicationController
# inflection_method :getit => :gender
# end
#
# # In this controller no method will be called
# # to obtain inflection option's value for the kind gender
# class FlowersController < ApplicationController
# no_inflection_method :getit
# end
#
# @api public
# @note Any added method will become a helper unless {I18n::Inflector::InflectionOptions#auto_helper} swtich is set to +false+!
# @raise [I18n::Inflector::Rails::BadInflectionMethod] when the given name or value are malformed
# @param [Hash{Symbol => Symbol},Array<Symbol>,Symbol,String] *args the methods and inflection kinds assigned to them
# @return [void]
def inflection_method(*args)
args = args.flatten
if args.empty?
raise I18n::Inflector::Rails::BadInflectionMethod.new(assignment)
end
assignment = {}
args.each do |e|
if (e.is_a?(Symbol) || e.is_a?(String))
assignment[e] = e
elsif e.is_a?(Hash)
raise I18n::Inflector::Rails::BadInflectionMethod.new(assignment) if e.empty?
assignment.merge!(e)
else
raise I18n::Inflector::Rails::BadInflectionMethod.new(assignment)
end
end
@i18n_inflector_kinds ||= {}
assignment.each_pair do |method, kind|
method = method.to_s
if (method.empty? || I18n::Inflector::Config::Reserved::Kinds.invalid?(kind, :OPTION))
raise I18n::Inflector::Rails::BadInflectionMethod.new("#{method.inspect} => #{kind.inspect}")
end
kind = kind.to_s
method = method[1..-1] if (method[0..0] == I18n::Inflector::Config::Markers::PATTERN && method == kind)
kind = kind.to_sym
method = method.to_sym
helper_method(method) if I18n.backend.inflector.options.auto_helper
@i18n_inflector_kinds[kind] = method
end
end
alias_method :inflection_methods, :inflection_method
# This method unregisters inflection kinds from assignments
# created by {inflection_method}. It is useful
# when there is a need to break inheritance in some controller,
# but there was a method assigned to some inflection kind in
# a parrent class.
#
# @api public
# @raise [I18n::Inflector::Rails::BadInflectionMethod] when name or value is bad or malformed
# @param [Array<Symbol>] names the method names for which the assigned kinds should be marked as not
# supported in a current controller and all derivative controllers
# @return [void]
def no_inflection_method(*names)
names = names.flatten
if (names.nil? || names.empty?)
raise I18n::Inflector::Rails::BadInflectionMethod.new(names)
end
@i18n_inflector_kinds ||= {}
names.each do |meth|
unless (meth.is_a?(Symbol) || meth.is_a?(String))
raise I18n::Inflector::Rails::BadInflectionMethod.new(meth)
end
meth = meth.to_s
meth = meth[1..-1] if meth[0..0] == I18n::Inflector::Config::Markers::PATTERN # for dummies
raise I18n::Inflector::Rails::BadInflectionMethod.new(meth) if meth.empty?
meth = meth.to_sym
i18n_inflector_kinds.each_pair do |kind, obj|
if obj == meth
@i18n_inflector_kinds[kind] = nil
end
end
end
end
alias_method :no_inflection_methods, :no_inflection_method
# This method unregisters the given inflection kinds from assignments
# created by {inflection_method}. It is useful
# when there is a need to break inheritance in some controller,
# but there was a method assigned to some inflection kind in
# a parrent class.
#
# @api public
# @raise [I18n::Inflector::Rails::BadInflectionMethod] when name or value is malformed
# @param [String,Symbol,Array<Symbol>] kinds the kind for which the method names should be marked
# as not supported in a current controller and all derivative controllers
# @return [void]
def no_inflection_method_for(*names)
names = names.flatten
if (names.nil? || names.empty?)
raise I18n::Inflector::Rails::BadInflectionMethod.new(names)
end
@i18n_inflector_kinds ||= {}
names.each do |kind|
unless (kind.is_a?(Symbol) || kind.is_a?(String))
raise I18n::Inflector::Rails::BadInflectionKind.new(kind)
end
if (I18n::Inflector::Config::Reserved::Kinds.invalid?(kind, :OPTION) ||
kind.to_s == I18n::Inflector::Config::Markers::PATTERN)
raise I18n::Inflector::Rails::BadInflectionKind.new(kind)
end
@i18n_inflector_kinds[kind.to_sym] = nil
end
end
alias_method :no_inflection_methods_for, :no_inflection_method_for
alias_method :no_inflection_kind, :no_inflection_method_for
end # class methods
# This module contains a variant of the +translate+ method that
# uses {I18n::Inflector::Rails::ClassMethods#i18n_inflector_kinds i18n_inflector_kinds}
# available in the current context.
# The method from this module will wrap the
# {ActionView::Helpers::TranslationHelper#translate} method.
module InflectedTranslate
# This method tries to feed itself with the data coming
# from {I18n::Inflector::Rails::ClassMethods#i18n_inflector_kinds i18n_inflector_kinds}
# available in the current context.
# That data contains inflection pairs (<tt>kind => value</tt>) that will
# be passed to the interpolation method from {I18n::Inflector} through
# {ActionView::Helpers::TranslationHelper#translate}.
#
# You may also pass inflection options directly, along with other options,
# without registering methods responsible for delivering tokens.
# See {I18n Inflector documentation}[http://rubydoc.info/gems/i18n-inflector]
# for more info about inflection options.
#
# @api public
# @raise {I18n::InvalidInflectionKind}
# @raise {I18n::InvalidInflectionOption}
# @raise {I18n::InvalidInflectionToken}
# @raise {I18n::MisplacedInflectionToken}
# @overload translate(key, options)
# @param [String] key translation key
# @param [Hash] options a set of options to pass to the
# translation routines
# @option options [Boolean] :inflector_verify_methods (false) local switch
# that overrides global setting (see {I18n::Inflector::InflectionOptions#verify_methods})
# @option options [Boolean] :inflector_lazy_methods (true) local switch
# that overrides global setting (see {I18n::Inflector::InflectionOptions#lazy_methods})
# @option options [Boolean] :inflector_excluded_defaults (false) local switch
# that overrides global setting (see {I18n Inflector documentation}[http://rubydoc.info/gems/i18n-inflector])
# @option options [Boolean] :inflector_unknown_defaults (true) local switch
# that overrides global setting (see {I18n Inflector documentation}[http://rubydoc.info/gems/i18n-inflector])
# @option options [Boolean] :inflector_raises (false) local switch
# that overrides global setting (see {I18n Inflector documentation}[http://rubydoc.info/gems/i18n-inflector])
# @option options [Boolean] :inflector_aliased_patterns (false) local switch
# that overrides global setting (see {I18n Inflector documentation}[http://rubydoc.info/gems/i18n-inflector])
# @option options [Boolean] :inflector_cache_aware (false) local switch
# that overrides global setting (see {I18n Inflector documentation}[http://rubydoc.info/gems/i18n-inflector])
# @return [String] the translated string with inflection patterns
# interpolated
def translate(*args, **options)
test_locale = options[:locale]
test_locale ||= I18n.locale
inflector = I18n.backend.inflector
# return immediately if the locale is not supported
return super unless inflector.inflected_locale?(test_locale)
# collect inflection variables that are present in this context
subopts = t_prepare_inflection_options(inflector, locale, options)
# jump to translate if no inflection options are present
return super if subopts.empty?
# pass options and call translate
options = subopts.merge(options)
super
end
# workaround for Ruby 1.8.x bug
if RUBY_VERSION.gsub(/\D/,'')[0..1].to_i < 19
def t(*args); translate(*args) end
else
alias_method :t, :translate
end
protected
# This method tries to read +i18n_inflector_kinds+ available in the current context.
#
# @return [Hash] the inflection options (<tt>kind => value</tt>)
def t_prepare_inflection_options(inflector, locale, options)
subopts = {}
verifies = options[:inflector_verify_methods]
verifies = inflector.options.verify_methods if verifies.nil?
is_lazy = options[:inflector_lazy_methods]
is_lazy = inflector.options.lazy_methods if is_lazy.nil?
return subopts if (verifies && !respond_to?(:i18n_inflector_kinds))
i18n_inflector_kinds.each_pair do |kind, meth|
next if meth.nil? # kind is registered but disabled from usage
next if verifies && !respond_to?(meth)
obj = method(meth)
obj = obj.call { next kind, locale } unless is_lazy # lazy_methods is disabled
subopts[kind] = obj
end
return subopts
end
end # module InflectedTranslate
end # module Rails
end # module Inflector
end # module I18n
|