1 # encoding: utf-8 2 3 # EXPERIMENTAL 4 # 5 # The Cascade module adds the ability to do cascading lookups to backends that 6 # are compatible to the Simple backend. 7 # 8 # By cascading lookups we mean that for any key that can not be found the 9 # Cascade module strips one segment off the scope part of the key and then 10 # tries to look up the key in that scope. 11 # 12 # E.g. when a lookup for the key :"foo.bar.baz" does not yield a result then 13 # the segment :bar will be stripped off the scope part :"foo.bar" and the new 14 # scope :foo will be used to look up the key :baz. If that does not succeed 15 # then the remaining scope segment :foo will be omitted, too, and again the 16 # key :baz will be looked up (now with no scope). 17 # 18 # To enable a cascading lookup one passes the :cascade option: 19 # 20 # I18n.t(:'foo.bar.baz', :cascade => true) 21 # 22 # This will return the first translation found for :"foo.bar.baz", :"foo.baz" 23 # or :baz in this order. 24 # 25 # The cascading lookup takes precedence over resolving any given defaults. 26 # I.e. defaults will kick in after the cascading lookups haven't succeeded. 27 # 28 # This behavior is useful for libraries like ActiveRecord validations where 29 # the library wants to give users a bunch of more or less fine-grained options 30 # of scopes for a particular key. 31 # 32 # Thanks to Clemens Kofler for the initial idea and implementation! See 33 # http://github.com/clemens/i18n-cascading-backend 34 35 module I18n 36 module Backend 37 module Cascade 38 def lookup(locale, key, scope = [], options = {}) 39 return super unless cascade = options[:cascade] 40 41 separator = options[:separator] || I18n.default_separator 42 skip_root = cascade.has_key?(:skip_root) ? cascade[:skip_root] : true 43 step = cascade[:step] 44 45 keys = I18n.normalize_keys(nil, key, nil, separator) 46 offset = options[:cascade][:offset] || keys.length 47 scope = I18n.normalize_keys(nil, nil, scope, separator) + keys 48 key = scope.slice!(-offset, offset).join(separator) 49 50 begin 51 result = super 52 return result unless result.nil? 53 end while !scope.empty? && scope.slice!(-step, step) && (!scope.empty? || !skip_root) 54 end 55 end 56 end 57 end