File: active_support/vendor/i18n-0.4.1/i18n.rb

Overview
Module Structure
Code

Overview

Module Structure

  module: <Toplevel Module>
  module: I18n#13
has properties
module method: config #21
module method: config= / 1 #26
module method: reload! #47
module method: translate / 1 #147
module alias: t translate #157
module method: translate! / 2 #159
module alias: t! translate! #162
module method: transliterate / 1 #215
module method: localize / 2 #228
module alias: l localize #233
module method: with_locale / 1 #236
module method: normalize_keys / 4 #250
module method: default_exception_handler / 4 #268
module method: handle_exception / 4 #290
module method: normalize_translation_keys (2/E) / 4 #301
module method: normalize_key / 2 #305
module method: normalized_key_cache #318

Code

   1  # encoding: utf-8
   2 
   3  # Authors::   Sven Fuchs (http://www.artweb-design.de),
   4  #             Joshua Harvey (http://www.workingwithrails.com/person/759-joshua-harvey),
   5  #             Stephan Soller (http://www.arkanis-development.de/),
   6  #             Saimon Moore (http://saimonmoore.net),
   7  #             Matt Aimonetti (http://railsontherun.com/)
   8  # Copyright:: Copyright (c) 2008 The Ruby i18n Team
   9  # License::   MIT
  10  require 'i18n/exceptions'
  11  require 'i18n/core_ext/string/interpolate'
  12 
  13  module I18n
  14    autoload :Backend, 'i18n/backend'
  15    autoload :Config,  'i18n/config'
  16    autoload :Gettext, 'i18n/gettext'
  17    autoload :Locale,  'i18n/locale'
  18 
  19    class << self
  20      # Gets I18n configuration object.
  21      def config
  22        Thread.current[:i18n_config] ||= I18n::Config.new
  23      end
  24 
  25      # Sets I18n configuration object.
  26      def config=(value)
  27        Thread.current[:i18n_config] = value
  28      end
  29 
  30      # Write methods which delegates to the configuration object
  31      %w(locale backend default_locale available_locales default_separator
  32        exception_handler load_path).each do |method|
  33        module_eval <<-DELEGATORS, __FILE__, __LINE__ + 1
  34          def #{method}
  35            config.#{method}
  36          end
  37 
  38          def #{method}=(value)
  39            config.#{method} = (value)
  40          end
  41        DELEGATORS
  42      end
  43 
  44      # Tells the backend to reload translations. Used in situations like the
  45      # Rails development environment. Backends can implement whatever strategy
  46      # is useful.
  47      def reload!
  48        config.backend.reload!
  49      end
  50 
  51      # Translates, pluralizes and interpolates a given key using a given locale,
  52      # scope, and default, as well as interpolation values.
  53      #
  54      # *LOOKUP*
  55      #
  56      # Translation data is organized as a nested hash using the upper-level keys
  57      # as namespaces. <em>E.g.</em>, ActionView ships with the translation:
  58      # <tt>:date => {:formats => {:short => "%b %d"}}</tt>.
  59      #
  60      # Translations can be looked up at any level of this hash using the key argument
  61      # and the scope option. <em>E.g.</em>, in this example <tt>I18n.t :date</tt>
  62      # returns the whole translations hash <tt>{:formats => {:short => "%b %d"}}</tt>.
  63      #
  64      # Key can be either a single key or a dot-separated key (both Strings and Symbols
  65      # work). <em>E.g.</em>, the short format can be looked up using both:
  66      #   I18n.t 'date.formats.short'
  67      #   I18n.t :'date.formats.short'
  68      #
  69      # Scope can be either a single key, a dot-separated key or an array of keys
  70      # or dot-separated keys. Keys and scopes can be combined freely. So these
  71      # examples will all look up the same short date format:
  72      #   I18n.t 'date.formats.short'
  73      #   I18n.t 'formats.short', :scope => 'date'
  74      #   I18n.t 'short', :scope => 'date.formats'
  75      #   I18n.t 'short', :scope => %w(date formats)
  76      #
  77      # *INTERPOLATION*
  78      #
  79      # Translations can contain interpolation variables which will be replaced by
  80      # values passed to #translate as part of the options hash, with the keys matching
  81      # the interpolation variable names.
  82      #
  83      # <em>E.g.</em>, with a translation <tt>:foo => "foo %{bar}"</tt> the option
  84      # value for the key +bar+ will be interpolated into the translation:
  85      #   I18n.t :foo, :bar => 'baz' # => 'foo baz'
  86      #
  87      # *PLURALIZATION*
  88      #
  89      # Translation data can contain pluralized translations. Pluralized translations
  90      # are arrays of singluar/plural versions of translations like <tt>['Foo', 'Foos']</tt>.
  91      #
  92      # Note that <tt>I18n::Backend::Simple</tt> only supports an algorithm for English
  93      # pluralization rules. Other algorithms can be supported by custom backends.
  94      #
  95      # This returns the singular version of a pluralized translation:
  96      #   I18n.t :foo, :count => 1 # => 'Foo'
  97      #
  98      # These both return the plural version of a pluralized translation:
  99      #   I18n.t :foo, :count => 0 # => 'Foos'
 100      #   I18n.t :foo, :count => 2 # => 'Foos'
 101      #
 102      # The <tt>:count</tt> option can be used both for pluralization and interpolation.
 103      # <em>E.g.</em>, with the translation
 104      # <tt>:foo => ['%{count} foo', '%{count} foos']</tt>, count will
 105      # be interpolated to the pluralized translation:
 106      #   I18n.t :foo, :count => 1 # => '1 foo'
 107      #
 108      # *DEFAULTS*
 109      #
 110      # This returns the translation for <tt>:foo</tt> or <tt>default</tt> if no translation was found:
 111      #   I18n.t :foo, :default => 'default'
 112      #
 113      # This returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt> if no
 114      # translation for <tt>:foo</tt> was found:
 115      #   I18n.t :foo, :default => :bar
 116      #
 117      # Returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt>
 118      # or <tt>default</tt> if no translations for <tt>:foo</tt> and <tt>:bar</tt> were found.
 119      #   I18n.t :foo, :default => [:bar, 'default']
 120      #
 121      # *BULK LOOKUP*
 122      #
 123      # This returns an array with the translations for <tt>:foo</tt> and <tt>:bar</tt>.
 124      #   I18n.t [:foo, :bar]
 125      #
 126      # Can be used with dot-separated nested keys:
 127      #   I18n.t [:'baz.foo', :'baz.bar']
 128      #
 129      # Which is the same as using a scope option:
 130      #   I18n.t [:foo, :bar], :scope => :baz
 131      #
 132      # *LAMBDAS*
 133      #
 134      # Both translations and defaults can be given as Ruby lambdas. Lambdas will be
 135      # called and passed the key and options.
 136      #
 137      # E.g. assuming the key <tt>:salutation</tt> resolves to:
 138      #   lambda { |key, options| options[:gender] == 'm' ? "Mr. %{options[:name]}" : "Mrs. %{options[:name]}" }
 139      #
 140      # Then <tt>I18n.t(:salutation, :gender => 'w', :name => 'Smith') will result in "Mrs. Smith".
 141      #
 142      # It is recommended to use/implement lambdas in an "idempotent" way. E.g. when
 143      # a cache layer is put in front of I18n.translate it will generate a cache key
 144      # from the argument values passed to #translate. Therefor your lambdas should
 145      # always return the same translations/values per unique combination of argument
 146      # values.
 147      def translate(*args)
 148        options = args.pop if args.last.is_a?(Hash)
 149        key     = args.shift
 150        locale  = options && options.delete(:locale) || config.locale
 151        raises  = options && options.delete(:raise)
 152        config.backend.translate(locale, key, options || {})
 153      rescue I18n::ArgumentError => exception
 154        raise exception if raises
 155        handle_exception(exception, locale, key, options)
 156      end
 157      alias :t :translate
 158 
 159      def translate!(key, options = {})
 160        translate(key, options.merge( :raise => true ))
 161      end
 162      alias :t! :translate!
 163 
 164      # Transliterates UTF-8 characters to ASCII. By default this method will
 165      # transliterate only Latin strings to an ASCII approximation:
 166      #
 167      #    I18n.transliterate("Ærøskøbing")
 168      #    # => "AEroskobing"
 169      #
 170      #    I18n.transliterate("日本語")
 171      #    # => "???"
 172      #
 173      # It's also possible to add support for per-locale transliterations. I18n
 174      # expects transliteration rules to be stored at
 175      # <tt>i18n.transliterate.rule</tt>.
 176      #
 177      # Transliteration rules can either be a Hash or a Proc. Procs must accept a
 178      # single string argument. Hash rules inherit the default transliteration
 179      # rules, while Procs do not.
 180      #
 181      # *Examples*
 182      #
 183      # Setting a Hash in <locale>.yml:
 184      #
 185      #    i18n:
 186      #      transliterate:
 187      #        rule:
 188      #          ü: "ue"
 189      #          ö: "oe"
 190      #
 191      # Setting a Hash using Ruby:
 192      #
 193      #     store_translations(:de, :i18n => {
 194      #       :transliterate => {
 195      #         :rule => {
 196      #           "ü" => "ue",
 197      #           "ö" => "oe"
 198      #         }
 199      #       }
 200      #     )
 201      #
 202      # Setting a Proc:
 203      #
 204      #     translit = lambda {|string| MyTransliterator.transliterate(string) }
 205      #     store_translations(:xx, :i18n => {:transliterate => {:rule => translit})
 206      #
 207      # Transliterating strings:
 208      #
 209      #     I18n.locale = :en
 210      #     I18n.transliterate("Jürgen") # => "Jurgen"
 211      #     I18n.locale = :de
 212      #     I18n.transliterate("Jürgen") # => "Juergen"
 213      #     I18n.transliterate("Jürgen", :locale => :en) # => "Jurgen"
 214      #     I18n.transliterate("Jürgen", :locale => :de) # => "Juergen"
 215      def transliterate(*args)
 216        options      = args.pop if args.last.is_a?(Hash)
 217        key          = args.shift
 218        locale       = options && options.delete(:locale) || config.locale
 219        raises       = options && options.delete(:raise)
 220        replacement  = options && options.delete(:replacement)
 221        config.backend.transliterate(locale, key, replacement)
 222      rescue I18n::ArgumentError => exception
 223        raise exception if raises
 224        handle_exception(exception, locale, key, options)
 225      end
 226 
 227      # Localizes certain objects, such as dates and numbers to local formatting.
 228      def localize(object, options = {})
 229        locale = options.delete(:locale) || config.locale
 230        format = options.delete(:format) || :default
 231        config.backend.localize(locale, object, format, options)
 232      end
 233      alias :l :localize
 234 
 235      # Executes block with given I18n.locale set.
 236      def with_locale(tmp_locale = nil)
 237        if tmp_locale
 238          current_locale = self.locale
 239          self.locale    = tmp_locale
 240        end
 241        yield
 242      ensure
 243        self.locale = current_locale if tmp_locale
 244      end
 245 
 246 
 247      # Merges the given locale, key and scope into a single array of keys.
 248      # Splits keys that contain dots into multiple keys. Makes sure all
 249      # keys are Symbols.
 250      def normalize_keys(locale, key, scope, separator = nil)
 251        separator ||= I18n.default_separator
 252 
 253        keys = []
 254        keys.concat normalize_key(locale, separator)
 255        keys.concat normalize_key(scope, separator)
 256        keys.concat normalize_key(key, separator)
 257        keys
 258      end
 259 
 260    # making these private until Ruby 1.9.2 can send to protected methods again
 261    # see http://redmine.ruby-lang.org/repositories/revision/ruby-19?rev=24280
 262    private
 263 
 264      # Handles exceptions raised in the backend. All exceptions except for
 265      # MissingTranslationData exceptions are re-raised. When a MissingTranslationData
 266      # was caught and the option :raise is not set the handler returns an error
 267      # message string containing the key/scope.
 268      def default_exception_handler(exception, locale, key, options)
 269        return exception.message if MissingTranslationData === exception
 270        raise exception
 271      end
 272 
 273      # Any exceptions thrown in translate will be sent to the @@exception_handler
 274      # which can be a Symbol, a Proc or any other Object.
 275      #
 276      # If exception_handler is a Symbol then it will simply be sent to I18n as
 277      # a method call. A Proc will simply be called. In any other case the
 278      # method #call will be called on the exception_handler object.
 279      #
 280      # Examples:
 281      #
 282      #   I18n.exception_handler = :default_exception_handler             # this is the default
 283      #   I18n.default_exception_handler(exception, locale, key, options) # will be called like this
 284      #
 285      #   I18n.exception_handler = lambda { |*args| ... }                 # a lambda
 286      #   I18n.exception_handler.call(exception, locale, key, options)    # will be called like this
 287      #
 288      #  I18n.exception_handler = I18nExceptionHandler.new                # an object
 289      #  I18n.exception_handler.call(exception, locale, key, options)     # will be called like this
 290      def handle_exception(exception, locale, key, options)
 291        case config.exception_handler
 292        when Symbol
 293          send(config.exception_handler, exception, locale, key, options)
 294        else
 295          config.exception_handler.call(exception, locale, key, options)
 296        end
 297      end
 298 
 299      # Deprecated. Will raise a warning in future versions and then finally be
 300      # removed. Use I18n.normalize_keys instead.
 301      def normalize_translation_keys(locale, key, scope, separator = nil)
 302        normalize_keys(locale, key, scope, separator)
 303      end
 304 
 305      def normalize_key(key, separator)
 306        normalized_key_cache[separator][key] ||=
 307          case key
 308          when Array
 309            key.map { |k| normalize_key(k, separator) }.flatten
 310          else
 311            keys = key.to_s.split(separator)
 312            keys.delete('')
 313            keys.map!{ |k| k.to_sym }
 314            keys
 315          end
 316      end
 317 
 318      def normalized_key_cache
 319        @normalized_key_cache ||= Hash.new { |h,k| h[k] = {} }
 320      end
 321    end
 322  end