The Ruby On Rails I18n Core Api

neerajkumar09 2,434 views 58 slides Jul 15, 2010
Slide 1
Slide 1 of 58
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30
Slide 31
31
Slide 32
32
Slide 33
33
Slide 34
34
Slide 35
35
Slide 36
36
Slide 37
37
Slide 38
38
Slide 39
39
Slide 40
40
Slide 41
41
Slide 42
42
Slide 43
43
Slide 44
44
Slide 45
45
Slide 46
46
Slide 47
47
Slide 48
48
Slide 49
49
Slide 50
50
Slide 51
51
Slide 52
52
Slide 53
53
Slide 54
54
Slide 55
55
Slide 56
56
Slide 57
57
Slide 58
58

About This Presentation

My presentation on I18n during Ruby India Conference 2010, Bangalore, India


Slide Content

The Ruby on Rails I18n The Ruby on Rails I18n
Core APICore API
PRESENTED BYPRESENTED BY
-Neeraj Kumar-Neeraj Kumar

IntroductionIntroduction
•English – Default Language.English – Default Language.
•GettextGettext
•Tough Task – transform your ROR App into its Tough Task – transform your ROR App into its
regional language & providing a tool to solve all regional language & providing a tool to solve all
problems at once.problems at once.

•Provides easy-to-use and extensible framework.Provides easy-to-use and extensible framework.
• Translating Translating
- to a single custom language other than English.- to a single custom language other than English.
- for providing multi-language support.- for providing multi-language support.

• To abstract all strings and other locale specific bits To abstract all strings and other locale specific bits
(such as date or currency formats) out of your (such as date or currency formats) out of your
application. The process of “localization” means to application. The process of “localization” means to
provide translations and localized formats for these bits.provide translations and localized formats for these bits.
•Sven FuchsSven Fuchs
•Shipped with rails (started with rails-2.2)Shipped with rails (started with rails-2.2)

• In the process of internationalizing: In the process of internationalizing:
–Ensure you have support for i18nEnsure you have support for i18n
–Tell Rails where to find locale dictionariesTell Rails where to find locale dictionaries
–Tell Rails how to set, preserve and switch localeTell Rails how to set, preserve and switch locale

SetupSetup for RoR App for RoR App
•Configure the I18n ModuleConfigure the I18n Module
–.rb and .yml + .rb and .yml + translations load pathtranslations load path, automatically., automatically.
–translations load path (translations load path (I18n.load_pathI18n.load_path) - will be ) - will be
loaded automatically and available in your loaded automatically and available in your
application.application.

–environment.rbenvironment.rb - instructions to customize the - instructions to customize the
locale directory and default locale.e.g.locale directory and default locale.e.g.
# config.i18n.load_path << # config.i18n.load_path <<
Dir[File.join(RAILS_ROOT, 'my', 'locales', '*.Dir[File.join(RAILS_ROOT, 'my', 'locales', '*.
{rb,yml}')]{rb,yml}')]
# config.i18n.default_locale = :de # config.i18n.default_locale = :de
en: en:
hello: "Hello world"hello: "Hello world"

•Setting and Passing the LocaleSetting and Passing the Locale
- For more locales - to set and pass the locale between - For more locales - to set and pass the locale between
requests.requests.
- Don't use session or cookies to store the chosen locale.- Don't use session or cookies to store the chosen locale.

before_filter :set_locale
def set_locale
# if params[:locale] is nil then
I18n.default_locale will be used
I18n.locale = params[:locale]
end
- URL : - URL : http://example.com/books?locale=pthttp://example.com/books?locale=pt - will load - will load
Portuguese localization.Portuguese localization.

•Locale setting from the URL ParamsLocale setting from the URL Params
- link_to( books_url(:locale => I18n.locale)) - tedious - link_to( books_url(:locale => I18n.locale)) - tedious
and probably impossible.and probably impossible.
- For 'centralizing dynamic decisions about the URLs' - For 'centralizing dynamic decisions about the URLs'
in its ApplicationController#default_url_options.in its ApplicationController#default_url_options.

# app/controllers/application_controller.rb
def default_url_options(options = {})
logger.debug “default_url_options is passed
options:
#{options.inspect}\n”
{:locale => I18n.locale}
end
- Every helper method dependent on url_for - Every helper method dependent on url_for
automatically include the locale in the query stringautomatically include the locale in the query string..

# config/routes.rb
map.resources :books, :path_prefix => '/:locale'
# => www.example.com/nl/books
map.root '/:locale', :controller => “dashboard”

- Drawback of default_url_options - Drawback of default_url_options
implementation pass the :id option, e.g. link_to implementation pass the :id option, e.g. link_to
'show', book_url(:id => book) 'show', book_url(:id => book)

•Locale setting from the Domain NameLocale setting from the Domain Name
- advantages- advantages
–Locale, an obvious part of URLLocale, an obvious part of URL
–Trivial to implementTrivial to implement
–Intuitively grasp the language of content Intuitively grasp the language of content
before loadingbefore loading
–Search engines like content in different Search engines like content in different
languages at different domains.languages at different domains.

before_filter :set_locale
def set_locale
I18n.locale = extract_locale_from_uri
end

def extract_locale_from_tld
parsed_locale = request.host.split('.').last
I18n.available_locales.include?
(parsed_locale.to_sym) ? parsed_locale : nil
end
- parsed_locale = request.host.split('.').first to set the - parsed_locale = request.host.split('.').first to set the
locale from subdomain.locale from subdomain.

•Locale setting from the Client Supplied InformationLocale setting from the Client Supplied Information
- information can come from - information can come from
–Users preferred language (set in their Users preferred language (set in their
browser)browser)
–Users geographical location inferred from IPUsers geographical location inferred from IP
–By choosing locale in your application By choosing locale in your application
interface and saving to the profile.interface and saving to the profile.

def set_locale
logger.debug = “* Accept-Language:
#{request.env['HTTP_ACCEPT_LANGUAGE']}”
I18n.locale =
extract_locale_from_accept_language_header
end

def extract_locale_from_accept_language_header
request.env['HTTP_ACCEPT_LANGUAGE' ].scan(/^[a-z]
{2}/).first
end
- Using GeoIP (or similar) Database – GeoIP Lite - Using GeoIP (or similar) Database – GeoIP Lite
CountryCountry
- User Profile- User Profile

en:
activerecord:
models:
user: foo
admin: bar
attributes:
user:
login: “Handle” # => will translate User
attribute “login” as “Handle”
Active Record Model TranslationsActive Record Model Translations

en:
activerecord:
errors:
messages:
blank: “can not has nothing”
# => #<User id:nil, etc>
# => u.valid?
# => false
# => u.errors.on(:name)
# => “can not has nothing”
–Error Messages ScopeError Messages Scope - Active Record - Active Record
Validation Error Messages TranslationValidation Error Messages Translation

–Active Record will look up this key in the Active Record will look up this key in the
namespacesnamespaces
–activerecord.errors.models.activerecord.errors.models.
[model_name].attributes.[attribute_name] [model_name].attributes.[attribute_name]
–activerecord.errors.models.[model_name] activerecord.errors.models.[model_name]
–activerecord.errors.messages activerecord.errors.messages

en:
activerecord:
errors:
messages:
already_registered: “u already is
{{model}}”
# => u.errors.on(:email)
# => “u already is foo”
–Error Message InterpolationError Message Interpolation
–Count can be used for PluralizationCount can be used for Pluralization

–Translation for the Active Record Translation for the Active Record
error_messages_for Helpererror_messages_for Helper
en:
activerecord:
errors:
template:
header:
one: “1 error prohibted this {{model}} from being
saved”
other: “{{count}} errors prohibted this {{model}} from
being saved”
body: “There were problems with the following fields:”

Anatomy of GemAnatomy of Gem
i18n
i18n
backend core_ext helpers locales
backend
exceptions
gettext
helpers
locale
i18n
version
locale
gettext
fallbackstag

active_record
Interpolation
compiler
Interpolation
compiler
Interpolation
compiler
Interpolation
compiler
Interpolation
compiler
Interpolation
compiler
Interpolation
compiler
fast
gettext
helpers
Interpolation
compiler
links
metadata
simpleactive_record
base
cache
cascader
chain
cldr
fallbacks pluralization
missing
translation
store_procs
backend

i18n.rbi18n.rb
•get and set methods for default_locale.get and set methods for default_locale.
def default_locale(locale)
@@default_locale = locale.to_sym rescue
nil
end
•Get method for locale - either default_locale or locale Get method for locale - either default_locale or locale
in in Thread.currentThread.current hash. hash.

i18n.rbi18n.rb
•Set method for locale - set the locale in Set method for locale - set the locale in Thread.currentThread.current
•Returns the current backend.Returns the current backend.
def backend
@@backend ||= Backend::Simple.new
end
•Set method for current backend.Set method for current backend.

simple.rbsimple.rb
•Makes easier to extend the Simple backend's Makes easier to extend the Simple backend's
behaviour by including modules e.g. behaviour by including modules e.g.
I18n::Backend::Simple.send(:include, I18n::Backend::Simple.send(:include,
I18n::Backend::Pluralization).I18n::Backend::Pluralization).
module I18n
module Backend
class Simple
include Base
end
end
end

i18n.rbi18n.rb
def available_locales
@@available_locales ||=
backend.available_locales
end

I18n.rbI18n.rb
•Default scope separator method (i.e Default scope separator method (i.e
default_separator) default_separator)
–set your separator set your separator
–return the separator.return the separator.
•Default is '.'Default is '.'

I18n.rbI18n.rb
•Exception handler method (i.e. exception_handler)Exception handler method (i.e. exception_handler)
–set your exception handlerset your exception handler
–return the current exception handler.return the current exception handler.
•Default is :default_exception_handler private method.Default is :default_exception_handler private method.

I18n.rbI18n.rb
def default_exception_handler(exception, locale, key,
options)
return exception.message if MissingTranslationData
=== exception
raise exception
end

I18n.rbI18n.rb
•load_path=(load_path) method - set load path instance.load_path=(load_path) method - set load path instance.
•load_path methodload_path method
–*.rb*.rb and contain plain and contain plain RubyRuby Hashes.Hashes.
–*.yml*.yml and contain and contain YAMLYAML data. data.

I18n.rbI18n.rb
•Class method config returns I18n configuration objectClass method config returns I18n configuration object
def config
Thread.current[:i18n_config] ||= I18n::Config.new
end
•method config=(value) sets I18n configuration object.method config=(value) sets I18n configuration object.

I18n.rbI18n.rb
•Method to reload the translations.Method to reload the translations.
•Main method Main method translatetranslate and and localizelocalize
•I18n.t :message I18n.t :message I18n.t 'message' I18n.t 'message'

I18n.rbI18n.rb
•Translation method – scope options, interpolation, Translation method – scope options, interpolation,
pluralization, defaults pluralization, defaults
•:scope option – one or many keys - scope for a :scope option – one or many keys - scope for a
translation keytranslation key
I18n.t :invalid, :scope => [:active_record,
:error_messages] # => I18n.translation :invalid
:active_record.error_messages.invalid

I18n.rbI18n.rb
•InterpolationInterpolation
I18n.t :foo, :bar => 'baz' #=> 'foo baz'
I18n.t :foo, :count => 1 #=> 'foo'
I18n.t :foo, :count => 0 #=> 'foos'
I18n.t :foo, :count => 2 #=> 'foos'
•PluralizationPluralization
•DefaultsDefaults
I18n.t :foo, :default => 'bar'

I18n.rbI18n.rb
def translate(&args)
options = args.pop if args.last.is_a?(Hash)
key = args.shift
locale = options && options.delete(:locale) ||
config.locale
raises = options && options.delete(:raise)
config.backend.translate(locale, key, options
|| {})
rescue I18n::ArgumentError => exception
raise exception if raises
handle_exception(exception, locale, key,
options)
end
alias :t :translate

I18n.rbI18n.rb
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

base.rbbase.rb
•load_translations method - accepts list of paths of load_translations method - accepts list of paths of
translation files.translation files.
def load_translations(*filenames)
filenames.each { |filename|
load_file(filename) }
end

base.rbbase.rb
def load_file(filename)
type = File.extname(filename).tr('.', '').downcase
raise UnknownFileType.new(type, filename)
unless respond_to?(:”load_#(type)”)
data = send(:”load_#(type)”, filename)
data.each { |locale, d| merge_translation(locale, d)
}
end

base.rbbase.rb
def store_translations(locale, data, options = {})
merge_translations(locale, data, options)
end

def merge_translations(locale, data, options = {})
locale = locale.to_sym
translations[locale] ||= {}
separator = options[:separator] ||
I18n.default_separator
data = unwind_keys(data, separator)
data = deep_symbolized_keys(data)
merger = proc do |key, v1, v2|
Hash === v1 && Hash === v2 ?
v1.merge(v2, &merger) : (v2 || v1)
end
translations[locale].merge!(data, &merger)
end
base.rbbase.rb

base.rbbase.rb
def translate(locale, key, options = {})
raise InvalidLocale.new(locale) unless locale
return key.map { |k| translate(locale, k, options) } if
key.is_a?(Array)
if options.empty?
entry = resolve(locale, key, lookup(locale, key),
options)
raise(I18n::MissingTranslationData.new(locale, key,
options)) if entry.nil?

else
count, scope, default =
options.values_at(:count, :scope, :default)
values = options.reject { |name, value|
RESERVED_KEYS .include?(name) }
entry = lookup(locale, key, scope, options)
entry = entry.nil? && default ?
default(locale, key, default, options) :
resolve(locale, key, entry, options)

base.rbbase.rb

raise(I18n::MissingTranslationData.new(locale,
key, options)) if entry.nil?
entry = pluralize(locale, entry, count) if
count
entry = interpolate(locale, entry, values) if
values
end
entry
end
base.rbbase.rb

base.rbbase.rb
•look_up method look_up method
–looks up the translation from the translations hash. looks up the translation from the translations hash.
–Splits keys or scopes containing dots into multiple Splits keys or scopes containing dots into multiple
keys e.g. currency.format - %w(currency format).keys e.g. currency.format - %w(currency format).

base.rbbase.rb


•localize methodlocalize method
case match
when '%a' then I18n.t(:"date.abbr_day_names”, :locale =>
locale, :format => format)[object.wday]
when '%A' then I18n.t(:"date.day_names”, :locale =>
locale, :format => format)[object.wday]
when '%b' then I18n.t(:"date.abbr_month_names", :locale
=> locale, :format => format)[object.mon]

when '%B' then I18n.t(:"date.month_names", :locale =>
locale, :format => format)[object.mon]
when '%p' then I18n.t(:"time.#{object.hour < 12 ? :am :
:pm}", :locale => locale, :format => format) if
object.respond_to? :hour
end

base.rbbase.rb

CustomizationCustomization
•Different BackendsDifferent Backends
–I18n::Backend::SimpleI18n::Backend::Simple
–shipped with Active Support of vendor shipped with Active Support of vendor
directory, work for english or similar directory, work for english or similar
languages.languages.
–capable of reading translations but cannot capable of reading translations but cannot
dynamically store them to any format.dynamically store them to any format.

MissingTranslationData # no translation was found for the
requested key
InvalidLocale # the locale set to I18n.locale is invalid (e.g. nil)
•Different Exception HandlersDifferent Exception Handlers

- customization – e.g. the default exception handling - customization – e.g. the default exception handling
does not allow to catch missing translation during does not allow to catch missing translation during
automated test easilyautomated test easily..

–I18nI18n
–own backendown backend
–makes easy to exchange the Simple backend makes easy to exchange the Simple backend
implementation with that to fits better with implementation with that to fits better with
your need.your need.
I18n.backend = Globalize::Backend::Static.new

module I18n
def just_raise_that_exception(*args)
raise args.first
end
end
I18n.exception_handler = :just_raise_that_exception

ReferencesReferences
•http://guides.rails.info/i18n.htmlhttp://guides.rails.info/i18n.html
•http://rails-i18n.org/wikihttp://rails-i18n.org/wiki
•http://iain.nl/2008/09/translating-activerecord/http://iain.nl/2008/09/translating-activerecord/
•http://github.com/svenfuchs/rails-i18nhttp://github.com/svenfuchs/rails-i18n
•http://rails-i18n.org/wiki/wikipages/i18n-rails-guidehttp://rails-i18n.org/wiki/wikipages/i18n-rails-guide
•http://www.artweb-design.de/2008/7/18/the-ruby-on-rails-i18n-core-apihttp://www.artweb-design.de/2008/7/18/the-ruby-on-rails-i18n-core-api

Questions?Questions?

Thank You!Thank You!