Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

executable file 333 lines (310 sloc) 12.922 kb
require 'i18n/version'
require 'i18n/exceptions'
require 'i18n/interpolate/ruby'
module I18n
autoload :Backend, 'i18n/backend'
autoload :Config, 'i18n/config'
autoload :Gettext, 'i18n/gettext'
autoload :Locale, 'i18n/locale'
autoload :Tests, 'i18n/tests'
RESERVED_KEYS = [:scope, :default, :separator, :resolve, :object, :fallback, :format, :cascade, :throw, :raise, :rescue_format]
RESERVED_KEYS_PATTERN = /%\{(#{RESERVED_KEYS.join("|")})\}/
extend(Module.new {
# Gets I18n configuration object.
def config
Thread.current[:i18n_config] ||= I18n::Config.new
end
# Sets I18n configuration object.
def config=(value)
Thread.current[:i18n_config] = value
end
# Write methods which delegates to the configuration object
%w(locale backend default_locale available_locales default_separator
exception_handler load_path).each do |method|
module_eval <<-DELEGATORS, __FILE__, __LINE__ + 1
def #{method}
config.#{method}
end
def #{method}=(value)
config.#{method} = (value)
end
DELEGATORS
end
# Tells the backend to reload translations. Used in situations like the
# Rails development environment. Backends can implement whatever strategy
# is useful.
def reload!
config.backend.reload!
end
# Translates, pluralizes and interpolates a given key using a given locale,
# scope, and default, as well as interpolation values.
#
# *LOOKUP*
#
# Translation data is organized as a nested hash using the upper-level keys
# as namespaces. <em>E.g.</em>, ActionView ships with the translation:
# <tt>:date => {:formats => {:short => "%b %d"}}</tt>.
#
# Translations can be looked up at any level of this hash using the key argument
# and the scope option. <em>E.g.</em>, in this example <tt>I18n.t :date</tt>
# returns the whole translations hash <tt>{:formats => {:short => "%b %d"}}</tt>.
#
# Key can be either a single key or a dot-separated key (both Strings and Symbols
# work). <em>E.g.</em>, the short format can be looked up using both:
# I18n.t 'date.formats.short'
# I18n.t :'date.formats.short'
#
# Scope can be either a single key, a dot-separated key or an array of keys
# or dot-separated keys. Keys and scopes can be combined freely. So these
# examples will all look up the same short date format:
# I18n.t 'date.formats.short'
# I18n.t 'formats.short', :scope => 'date'
# I18n.t 'short', :scope => 'date.formats'
# I18n.t 'short', :scope => %w(date formats)
#
# *INTERPOLATION*
#
# Translations can contain interpolation variables which will be replaced by
# values passed to #translate as part of the options hash, with the keys matching
# the interpolation variable names.
#
# <em>E.g.</em>, with a translation <tt>:foo => "foo %{bar}"</tt> the option
# value for the key +bar+ will be interpolated into the translation:
# I18n.t :foo, :bar => 'baz' # => 'foo baz'
#
# *PLURALIZATION*
#
# Translation data can contain pluralized translations. Pluralized translations
# are arrays of singluar/plural versions of translations like <tt>['Foo', 'Foos']</tt>.
#
# Note that <tt>I18n::Backend::Simple</tt> only supports an algorithm for English
# pluralization rules. Other algorithms can be supported by custom backends.
#
# This returns the singular version of a pluralized translation:
# I18n.t :foo, :count => 1 # => 'Foo'
#
# These both return the plural version of a pluralized translation:
# I18n.t :foo, :count => 0 # => 'Foos'
# I18n.t :foo, :count => 2 # => 'Foos'
#
# The <tt>:count</tt> option can be used both for pluralization and interpolation.
# <em>E.g.</em>, with the translation
# <tt>:foo => ['%{count} foo', '%{count} foos']</tt>, count will
# be interpolated to the pluralized translation:
# I18n.t :foo, :count => 1 # => '1 foo'
#
# *DEFAULTS*
#
# This returns the translation for <tt>:foo</tt> or <tt>default</tt> if no translation was found:
# I18n.t :foo, :default => 'default'
#
# This returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt> if no
# translation for <tt>:foo</tt> was found:
# I18n.t :foo, :default => :bar
#
# Returns the translation for <tt>:foo</tt> or the translation for <tt>:bar</tt>
# or <tt>default</tt> if no translations for <tt>:foo</tt> and <tt>:bar</tt> were found.
# I18n.t :foo, :default => [:bar, 'default']
#
# *BULK LOOKUP*
#
# This returns an array with the translations for <tt>:foo</tt> and <tt>:bar</tt>.
# I18n.t [:foo, :bar]
#
# Can be used with dot-separated nested keys:
# I18n.t [:'baz.foo', :'baz.bar']
#
# Which is the same as using a scope option:
# I18n.t [:foo, :bar], :scope => :baz
#
# *LAMBDAS*
#
# Both translations and defaults can be given as Ruby lambdas. Lambdas will be
# called and passed the key and options.
#
# E.g. assuming the key <tt>:salutation</tt> resolves to:
# lambda { |key, options| options[:gender] == 'm' ? "Mr. %{options[:name]}" : "Mrs. %{options[:name]}" }
#
# Then <tt>I18n.t(:salutation, :gender => 'w', :name => 'Smith') will result in "Mrs. Smith".
#
# It is recommended to use/implement lambdas in an "idempotent" way. E.g. when
# a cache layer is put in front of I18n.translate it will generate a cache key
# from the argument values passed to #translate. Therefor your lambdas should
# always return the same translations/values per unique combination of argument
# values.
def translate(*args)
options = args.last.is_a?(Hash) ? args.pop : {}
key = args.shift
backend = config.backend
locale = options.delete(:locale) || config.locale
handling = options.delete(:throw) && :throw || options.delete(:raise) && :raise # TODO deprecate :raise
raise I18n::ArgumentError if key.is_a?(String) && key.empty?
result = catch(:exception) do
if key.is_a?(Array)
key.map { |k| backend.translate(locale, k, options) }
else
backend.translate(locale, key, options)
end
end
result.is_a?(MissingTranslation) ? handle_exception(handling, result, locale, key, options) : result
end
alias :t :translate
# Wrapper for <tt>translate</tt> that adds <tt>:raise => true</tt>. With
# this option, if no translation is found, it will raise <tt>I18n::MissingTranslationData</tt>
def translate!(key, options={})
translate(key, options.merge(:raise => true))
end
alias :t! :translate!
# Transliterates UTF-8 characters to ASCII. By default this method will
# transliterate only Latin strings to an ASCII approximation:
#
# I18n.transliterate("Ærøskøbing")
# # => "AEroskobing"
#
# I18n.transliterate("日本語")
# # => "???"
#
# It's also possible to add support for per-locale transliterations. I18n
# expects transliteration rules to be stored at
# <tt>i18n.transliterate.rule</tt>.
#
# Transliteration rules can either be a Hash or a Proc. Procs must accept a
# single string argument. Hash rules inherit the default transliteration
# rules, while Procs do not.
#
# *Examples*
#
# Setting a Hash in <locale>.yml:
#
# i18n:
# transliterate:
# rule:
# ü: "ue"
# ö: "oe"
#
# Setting a Hash using Ruby:
#
# store_translations(:de, :i18n => {
# :transliterate => {
# :rule => {
# "ü" => "ue",
# "ö" => "oe"
# }
# }
# )
#
# Setting a Proc:
#
# translit = lambda {|string| MyTransliterator.transliterate(string) }
# store_translations(:xx, :i18n => {:transliterate => {:rule => translit})
#
# Transliterating strings:
#
# I18n.locale = :en
# I18n.transliterate("Jürgen") # => "Jurgen"
# I18n.locale = :de
# I18n.transliterate("Jürgen") # => "Juergen"
# I18n.transliterate("Jürgen", :locale => :en) # => "Jurgen"
# I18n.transliterate("Jürgen", :locale => :de) # => "Juergen"
def transliterate(*args)
options = args.pop if args.last.is_a?(Hash)
key = args.shift
locale = options && options.delete(:locale) || config.locale
handling = options && (options.delete(:throw) && :throw || options.delete(:raise) && :raise)
replacement = options && options.delete(:replacement)
config.backend.transliterate(locale, key, replacement)
rescue I18n::ArgumentError => exception
handle_exception(handling, exception, locale, key, options || {})
end
# Localizes certain objects, such as dates and numbers to local formatting.
def localize(object, options = {})
locale = options.delete(:locale) || config.locale
format = options.delete(:format) || :default
config.backend.localize(locale, object, format, options)
end
alias :l :localize
# Executes block with given I18n.locale set.
def with_locale(tmp_locale = nil)
if tmp_locale
current_locale = self.locale
self.locale = tmp_locale
end
yield
ensure
self.locale = current_locale if tmp_locale
end
# Merges the given locale, key and scope into a single array of keys.
# Splits keys that contain dots into multiple keys. Makes sure all
# keys are Symbols.
def normalize_keys(locale, key, scope, separator = nil)
separator ||= I18n.default_separator
keys = []
keys.concat normalize_key(locale, separator)
keys.concat normalize_key(scope, separator)
keys.concat normalize_key(key, separator)
keys
end
# making these private until Ruby 1.9.2 can send to protected methods again
# see http://redmine.ruby-lang.org/repositories/revision/ruby-19?rev=24280
private
# Any exceptions thrown in translate will be sent to the @@exception_handler
# which can be a Symbol, a Proc or any other Object unless they're forced to
# be raised or thrown (MissingTranslation).
#
# If exception_handler is a Symbol then it will simply be sent to I18n as
# a method call. A Proc will simply be called. In any other case the
# method #call will be called on the exception_handler object.
#
# Examples:
#
# I18n.exception_handler = :default_exception_handler # this is the default
# I18n.default_exception_handler(exception, locale, key, options) # will be called like this
#
# I18n.exception_handler = lambda { |*args| ... } # a lambda
# I18n.exception_handler.call(exception, locale, key, options) # will be called like this
#
# I18n.exception_handler = I18nExceptionHandler.new # an object
# I18n.exception_handler.call(exception, locale, key, options) # will be called like this
def handle_exception(handling, exception, locale, key, options)
case handling
when :raise
raise(exception.respond_to?(:to_exception) ? exception.to_exception : exception)
when :throw
throw :exception, exception
else
case handler = options[:exception_handler] || config.exception_handler
when Symbol
send(handler, exception, locale, key, options)
else
handler.call(exception, locale, key, options)
end
end
end
def normalize_key(key, separator)
normalized_key_cache[separator][key] ||=
case key
when Array
key.map { |k| normalize_key(k, separator) }.flatten
else
keys = key.to_s.split(separator)
keys.delete('')
keys.map! { |k| k.to_sym }
keys
end
end
def normalized_key_cache
@normalized_key_cache ||= Hash.new { |h,k| h[k] = {} }
end
# DEPRECATED. Use I18n.normalize_keys instead.
def normalize_translation_keys(locale, key, scope, separator = nil)
puts "I18n.normalize_translation_keys is deprecated. Please use the class I18n.normalize_keys instead."
normalize_keys(locale, key, scope, separator)
end
# DEPRECATED. Please use the I18n::ExceptionHandler class instead.
def default_exception_handler(exception, locale, key, options)
puts "I18n.default_exception_handler is deprecated. Please use the class I18n::ExceptionHandler instead " +
"(an instance of which is set to I18n.exception_handler by default)."
exception.is_a?(MissingTranslation) ? exception.message : raise(exception)
end
})
end
Jump to Line
Something went wrong with that request. Please try again.