SlideShare una empresa de Scribd logo
1 de 34
Descargar para leer sin conexión
Advanced
Internationalization
with Rails


            Clinton R. Nixon
                  Viget Labs
Hola! Qué tal?
Me llamo Clinton R. Nixon.
Soy programador de software.
Vivo en los Estados Unidos.
Mi cuidad es de Durham en Carolina del Norte.
Hablo un poco de español.
Entiendo un poco mas.
Me encanta Ruby y Rails.
The current state of Rails i18n
Key-value lookup
ActiveRecord support
Interpolation
Weak pluralization
Localized formats
Localized views (index.es.html.erb)
Key-value lookup
en:
    site_title:  The  Lost  Tourist
    add_tourism_office:  Add  a  New  Tourism  Office
    add_city:  Add  a  New  City
es:
    site_title:  El  Turista  que  se  Pierde
    add_tourism_office:  Crear  una  Nueva  Oficina  de  Turismo
    add_city:  Crear  una  Nueva  Cuidad



link_to(t(:site_title),  root_url)
ActiveRecord support
es:
    activerecord:
        errors:
            messages:
                phone_number:  "no  se  puede  en  blanco  a  menos  que  el  número  de  
teléfono  de  trabajo,  número  de  teléfono  móvil,  o  otro  número  de  teléfono"
        models:
            business:  Negocio
            business_plan:  Plan  de  Negocios
            business_process:  Función  Crítica
        attributes:
            pet:
                name:  Nombre
                species:  Especies
                special_needs:  Este  Mascota  Tiene  Necesidades  Especiales  Médicas
Interpolation


es:
    welcome_to:  "Bienvenido  a  {{city}}!"

t(:welcome_to,  :city  =>  @city.name)
Pluralization

es:
    city_has_x_tourism_offices:  
        one:  "Hay  {{count}}  oficina  de  turismo  de  {{city}}."
        other:  "Hay  {{count}}  oficinas  de  turismo  de  {{city}}."

t(:city_has_x_tourism_offices,  :city  =>  
@city.name,  :count  =>  @city.tourism_offices.count)
Localized formats
es:
    number:
        format:
            #  Sets  the  separator  between  the  units  (e.g.  1.0  /  2.0  ==  0.5)
            separator:  ","  
            #  Delimits  thousands  (e.g.  1,000,000  is  a  million)
            delimiter:  "."  
            #  Number  of  decimals  (1  with  a  precision  of  2  gives:  1.00)
            precision:  3

        #  Used  in  number_to_currency()
        currency:
            format:
                #  %u  is  the  currency  unit,  %n  the  number  (default:  $5.00)
                format:  "%n  %u"  
                unit:  "€"  
                #  These  three  are  to  override  number.format  and  are  optional
                separator:  ","  
                delimiter:  "."  
                precision:  2
Localized views


app/
    views/
        cities/
            show.haml        #  used  by  default  locale
            show.es.haml  #  used  by  :es  locale
Difficulties


File-based key-value lookup is not user-editable.
Model attributes are not translated.
The Rails inflector is not multi-lingual.
Plugins to help


Newsdesk Translate
translatable_columns
Globalize2
Model attribute translations
test  "returns  the  value  for  the  correct  locale"  do
    post  =  Post.create  :subject  =>  'foo'
    I18n.locale  =  'de'
    post.subject  =  'bar'
    post.save
    I18n.locale  =  'en'
    post  =  Post.first
    assert_equal  'foo',  post.subject
    I18n.locale  =  'de'
    assert_equal  'bar',  post.subject
end
Other Globalize2 features
Locale fallbacks
    I18n.fallbacks[:"es-­‐MX"]  #  =>  [:"es-­‐MX",  :es,  :"en-­‐US",  :en]

Custom pluralization
    @backend.add_pluralizer  :cz,  lambda{|c|  
        c  ==  1  ?  :one  :  (2..4).include?(c)  ?  :few  :  :other  
    }

Metadata
    rails  =  I18n.t  :rails    #  if  no  translation  can  be  found:
    rails.locale                      #  =>  :en
    rails.requested_locale  #  =>  :es  
    rails.fallback?                #  =>  true
    rails.options                    #  returns  the  options  passed  to  #t
    rails.plural_key              #  returns  the  plural_key  (e.g.  :one,  :other)
    rails.original                  #  returns  the  original  translation
Translating models


Globalize2 and translatable_models
work well for this.
How to start building a backend
The easiest way is to extend
I18n::Backend::Simple.
Key methods are:
init_translations  (protected)
store_translations
translate
localize
available_locales
Example: DB-backed backend
class  SnippetBackend  <  I18n::Backend::Simple
    #  These  are  the  only  supported  locales
    LOCALES  =  [:en,  :es]  
    
    protected

    def  init_translations
        load_translations(*I18n.load_path.flatten)
        load_translations_from_database
        @initialized  =  true
    end
    #  ...
end
Example: DB-backed backend
class  SnippetBackend  <  I18n::Backend::Simple
    #  These  are  the  only  supported  locales
    LOCALES  =  [:en,  :es]  
    
    protected

    def  init_translations
        load_translations(*I18n.load_path.flatten)
        load_translations_from_database
        @initialized  =  true
    end
    #  ...
end
Storing the translations
class  Snippet  <  ActiveRecord::Base
    validates_presence_of  :name,  :en_text,  :es_text
    validates_uniqueness_of  :name
    validate  :name_must_not_be_subset_of_other_name

    private
    def  name_must_not_be_subset_of_other_name
        if  self.name  =~  /./
            snippets  =  Snippet.find(:all,  
                    :conditions  =>  ['name  LIKE  ?',  "#{self.name}%"])
            snippets.reject!  {  |s|  s.id  ==  self.id  }  unless  self.new_record?

            unless  snippets.empty?
                errors.add(:name,  
                    "must  not  be  a  subset  of  another  snippet  name")
            end
        end
    end
end
Loading translations from DB
   def  load_translations_from_database
       data  =  {  }
       LOCALES.each  {  |locale|  data[locale]  =  {}  }

       Snippet.all.each  do  |snippet|
           path  =  snippet.name.split(".")
           key  =  path.pop
           current  =  {}
           LOCALES.each  {  |locale|  current[locale]  =  data[locale]  }

           path.each  do  |group|
               LOCALES.each  do  |locale|  
                   current[locale][group]  ||=  {}
                   current[locale]  =  current[locale][group]
               end
           end

              LOCALES.each  {  |locale|  
            current[locale][key]  =  snippet.read_attribute("#{locale}_text")  }                
    end
    data.each  {  |locale,  d|  merge_translations(locale,  d)  }
end
Example: a bad idea
module  Traductor
    class  GoogleBackend  <  
I18n::Backend::Simple
        def  translate(locale,  key,  options  =  
{})
            if  locale.to_s  ==  'en'
                key
            else
                Translate.t(key,  'en',  locale.to_s)
            end
        end
    end
Translation data format
  {:en  =>  {  
      "pets"  =>  {  "canine"  =>  "dog",  "feline"  =>  "cat"  },
      "greeting"  =>  "Hello!",
      "farewell"  =>  "Goodbye!"  },
    :es  =>  {
        "pets"  =>  {  "canine"  =>  "perro",  "feline"  =>  
  "gato"  },
        "greeting"  =>  "¡Hola!",
        "farewell"  =>  "¡Hasta  luego!"  }  }


One could override merge_translations if you needed to
bring in data in another format.
Improving inflections


Let’s make the Rails inflector multilingual!
This will be incredibly invasive, full of monkey-
patching, and quite dangerous.
Why?


Generated forms: Formtastic, Typus
%h2=  City.human_name.pluralize
Black magic
module  ActiveSupport::Inflector
    def  inflections(locale  =  nil)
        locale  ||=  I18n.locale
        locale_class  =  ActiveSupport::Inflector.const_get(
            "Inflections_#{locale}")  rescue  nil

        if  locale_class.nil?
            ActiveSupport::Inflector.module_eval  %{
                class  ActiveSupport::Inflector::Inflections_#{locale}  <  
ActiveSupport::Inflector::Inflections
                end
            }
            locale_class  =  ActiveSupport::Inflector.const_get(
                "Inflections_#{locale}")
        end

        block_given?  ?  yield  locale_class.instance  :  locale_class.instance
    end
Black magic
module  ActiveSupport::Inflector
    def  inflections(locale  =  nil)
        locale  ||=  I18n.locale
        locale_class  =  ActiveSupport::Inflector.const_get(
            "Inflections_#{locale}")  rescue  nil

        if  locale_class.nil?
            ActiveSupport::Inflector.module_eval  %{
                class  ActiveSupport::Inflector::Inflections_#{locale}  <  
ActiveSupport::Inflector::Inflections
                end
            }
            locale_class  =  ActiveSupport::Inflector.const_get(
                "Inflections_#{locale}")
        end

        block_given?  ?  yield  locale_class.instance  :  locale_class.instance
    end
Black magic
module  ActiveSupport::Inflector
    def  inflections(locale  =  nil)
        locale  ||=  I18n.locale
        locale_class  =  ActiveSupport::Inflector.const_get(
            "Inflections_#{locale}")  rescue  nil

        if  locale_class.nil?
            ActiveSupport::Inflector.module_eval  %{
                class  ActiveSupport::Inflector::Inflections_#{locale}  <  
ActiveSupport::Inflector::Inflections
                end
            }
            locale_class  =  ActiveSupport::Inflector.const_get(
                "Inflections_#{locale}")
        end

        block_given?  ?  yield  locale_class.instance  :  locale_class.instance
    end
Success!
ActiveSupport::Inflector.inflections(:es)  do  |inflect|
    inflect.plural  /$/,  's'
    inflect.plural  /([^aeioué])$/,  '1es'
    inflect.plural  /([aeiou]s)$/,  '1'
    inflect.plural  /z$/,  'ces'
    inflect.plural  /á([sn])$/,  'a1es'
    inflect.plural  /í([sn])$/,  'i1es'
    inflect.plural  /ó([sn])$/,  'o1es'
    inflect.plural  /ú([sn])$/,  'u1es'
    inflect.singular  /s$/,  ''
    inflect.singular  /es$/,  ''
    inflect.irregular('papá',  'papás')
    inflect.irregular('mamá',  'mamás')
    inflect.irregular('sofá',  'sofás')
end
A minor problem

"oficina  de  turismo".pluralize  
#  =>  "oficina  de  turismos"

"contacto  de  emergencia".pluralize
#  =>  "contacto  de  emergencias"
Lambdas to the rescue
def  pluralize(word,  locale  =  nil)
    locale  ||=  I18n.locale
    result  =  word.to_s.dup

    #  ...  elided  ...
    inflections(locale).plurals.each  do  |(rule,  replacement)|
        if  replacement.respond_to?(:call)
            break  if  result.gsub!(rule,  &replacement)
        else
            break  if  result.gsub!(rule,  replacement)
        end
    end
    result
end
Lambdas to the rescue

ActiveSupport::Inflector.inflections(:es)  do  |inflect|
    #  ...  elided  ...
    inflect.plural(/^(w+)s(.+)$/,  lambda  {  |match|
                                      head,  tail  =  match.split(/s+/,  2)
                                      "#{head.pluralize}  #{tail}"
                                  })
    #  ...  elided  ...    
end
Success!
I18n.locale  =  :es

"oficina  de  turismo".pluralize  
#  =>  "oficinas  de  turismo"

"contacto  de  emergencia".pluralize
#  =>  "contactos  de  emergencia"

I18n.locale  =  :en

"emergency  contact".pluralize
#  =>  "emergency  contacts"
The inflector is used everywhere

Because of the wide-spread use of the inflector,
we have to patch:
ActiveSupport::ModelName#initialize
String#pluralize,  singularize
AS::Inflector#pluralize,  singularize,  tableize
ActiveRecord::Base.human_name,  undecorated_table_name
AR::Reflection::AssociationReflection#derive_class_name
ActionController::PolymorphicRoutes#build_named_route_call
An interesting side-effect
ActiveRecord::Schema.define  do
    create_table  "oficinas_de_turismo"  do  |t|
        t.string  "nombre"
        t.string  "calle"
    end
end

context  "En  español"  do
    setup  {  I18n.default_locale  =  :es  }

    context  "OficinaDeTurismo"  do
        should("use  the  right  table")  {  
            OficinaDeTurismo.table_name  }.equals  "oficinas_de_turismo"
    end
end
Muchas gracias
a todos!


                            crnixon@gmail.com
                              http://crnixon.org
                http://github.com/crnixon/traductor
http://delicious.com/crnixon/conferencia.rails+i18n

Más contenido relacionado

La actualidad más candente

Session tracking in servlets
Session tracking in servletsSession tracking in servlets
Session tracking in servletsvishal choudhary
 
Java Web Programming [1/9] : Introduction to Web Application
Java Web Programming [1/9] : Introduction to Web ApplicationJava Web Programming [1/9] : Introduction to Web Application
Java Web Programming [1/9] : Introduction to Web ApplicationIMC Institute
 
AngularJS Directives
AngularJS DirectivesAngularJS Directives
AngularJS DirectivesEyal Vardi
 
Android share preferences
Android share preferencesAndroid share preferences
Android share preferencesAjay Panchal
 
Collections In Java
Collections In JavaCollections In Java
Collections In JavaBinoj T E
 
Asp.net mvc 基礎
Asp.net mvc 基礎Asp.net mvc 基礎
Asp.net mvc 基礎Gelis Wu
 
New Elements & Features in HTML5
New Elements & Features in HTML5New Elements & Features in HTML5
New Elements & Features in HTML5Jamshid Hashimi
 
Java design patterns
Java design patternsJava design patterns
Java design patternsShawn Brito
 
Domain Driven Design com Python
Domain Driven Design com PythonDomain Driven Design com Python
Domain Driven Design com PythonFrederico Cabral
 
Multi-threaded Programming in JAVA
Multi-threaded Programming in JAVAMulti-threaded Programming in JAVA
Multi-threaded Programming in JAVAVikram Kalyani
 
Uml diagrams
Uml diagramsUml diagrams
Uml diagramsbarney92
 
Sequence diagram smart stock business
Sequence diagram  smart stock businessSequence diagram  smart stock business
Sequence diagram smart stock businesstasnimmohiuddin
 
Object Oriented Analysis Design using UML
Object Oriented Analysis Design using UMLObject Oriented Analysis Design using UML
Object Oriented Analysis Design using UMLAjit Nayak
 
How to get http query parameters in mule
How to get http query parameters in muleHow to get http query parameters in mule
How to get http query parameters in muleRamakrishna kapa
 
Advance Java Topics (J2EE)
Advance Java Topics (J2EE)Advance Java Topics (J2EE)
Advance Java Topics (J2EE)slire
 

La actualidad más candente (20)

Introduction To C#
Introduction To C#Introduction To C#
Introduction To C#
 
Session tracking in servlets
Session tracking in servletsSession tracking in servlets
Session tracking in servlets
 
Introduction to SQL
Introduction to SQLIntroduction to SQL
Introduction to SQL
 
Java Web Programming [1/9] : Introduction to Web Application
Java Web Programming [1/9] : Introduction to Web ApplicationJava Web Programming [1/9] : Introduction to Web Application
Java Web Programming [1/9] : Introduction to Web Application
 
AngularJS Directives
AngularJS DirectivesAngularJS Directives
AngularJS Directives
 
Android share preferences
Android share preferencesAndroid share preferences
Android share preferences
 
Collections In Java
Collections In JavaCollections In Java
Collections In Java
 
Sql Server Basics
Sql Server BasicsSql Server Basics
Sql Server Basics
 
Asp.net mvc 基礎
Asp.net mvc 基礎Asp.net mvc 基礎
Asp.net mvc 基礎
 
New Elements & Features in HTML5
New Elements & Features in HTML5New Elements & Features in HTML5
New Elements & Features in HTML5
 
Java design patterns
Java design patternsJava design patterns
Java design patterns
 
Domain Driven Design com Python
Domain Driven Design com PythonDomain Driven Design com Python
Domain Driven Design com Python
 
Multi-threaded Programming in JAVA
Multi-threaded Programming in JAVAMulti-threaded Programming in JAVA
Multi-threaded Programming in JAVA
 
Uml diagrams
Uml diagramsUml diagrams
Uml diagrams
 
Vb.net ide
Vb.net ideVb.net ide
Vb.net ide
 
Sequence diagram smart stock business
Sequence diagram  smart stock businessSequence diagram  smart stock business
Sequence diagram smart stock business
 
Object Oriented Analysis Design using UML
Object Oriented Analysis Design using UMLObject Oriented Analysis Design using UML
Object Oriented Analysis Design using UML
 
MySQL for beginners
MySQL for beginnersMySQL for beginners
MySQL for beginners
 
How to get http query parameters in mule
How to get http query parameters in muleHow to get http query parameters in mule
How to get http query parameters in mule
 
Advance Java Topics (J2EE)
Advance Java Topics (J2EE)Advance Java Topics (J2EE)
Advance Java Topics (J2EE)
 

Similar a Advanced Internationalization with Rails

The ruby on rails i18n core api-Neeraj Kumar
The ruby on rails i18n core api-Neeraj KumarThe ruby on rails i18n core api-Neeraj Kumar
The ruby on rails i18n core api-Neeraj KumarThoughtWorks
 
Rails World 2023: Powerful Rails Features You Might Not Know
Rails World 2023: Powerful Rails Features You Might Not KnowRails World 2023: Powerful Rails Features You Might Not Know
Rails World 2023: Powerful Rails Features You Might Not KnowChris Oliver
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applicationselliando dias
 
Postobjektové programovanie v Ruby
Postobjektové programovanie v RubyPostobjektové programovanie v Ruby
Postobjektové programovanie v RubyJano Suchal
 
Introducing Elixir and OTP at the Erlang BASH
Introducing Elixir and OTP at the Erlang BASHIntroducing Elixir and OTP at the Erlang BASH
Introducing Elixir and OTP at the Erlang BASHdevbash
 
The Ruby On Rails I18n Core Api
The Ruby On Rails I18n Core ApiThe Ruby On Rails I18n Core Api
The Ruby On Rails I18n Core ApiNTT DATA Americas
 
I18n
I18nI18n
I18nsoon
 
Zend Certification PHP 5 Sample Questions
Zend Certification PHP 5 Sample QuestionsZend Certification PHP 5 Sample Questions
Zend Certification PHP 5 Sample QuestionsJagat Kothari
 
Drupaljam xl 2019 presentation multilingualism makes better programmers
Drupaljam xl 2019 presentation   multilingualism makes better programmersDrupaljam xl 2019 presentation   multilingualism makes better programmers
Drupaljam xl 2019 presentation multilingualism makes better programmersAlexander Varwijk
 
Type safe embedded domain-specific languages
Type safe embedded domain-specific languagesType safe embedded domain-specific languages
Type safe embedded domain-specific languagesArthur Xavier
 
Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript Using CoffeeScript, Backbone.js and JasmineRails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript Using CoffeeScript, Backbone.js and JasmineRaimonds Simanovskis
 
Morel, a data-parallel programming language
Morel, a data-parallel programming languageMorel, a data-parallel programming language
Morel, a data-parallel programming languageJulian Hyde
 
Lithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksLithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksNate Abele
 

Similar a Advanced Internationalization with Rails (20)

The ruby on rails i18n core api-Neeraj Kumar
The ruby on rails i18n core api-Neeraj KumarThe ruby on rails i18n core api-Neeraj Kumar
The ruby on rails i18n core api-Neeraj Kumar
 
SOLID Ruby, SOLID Rails
SOLID Ruby, SOLID RailsSOLID Ruby, SOLID Rails
SOLID Ruby, SOLID Rails
 
Scala fundamentals
Scala fundamentalsScala fundamentals
Scala fundamentals
 
Ruby on Rails
Ruby on RailsRuby on Rails
Ruby on Rails
 
Rails World 2023: Powerful Rails Features You Might Not Know
Rails World 2023: Powerful Rails Features You Might Not KnowRails World 2023: Powerful Rails Features You Might Not Know
Rails World 2023: Powerful Rails Features You Might Not Know
 
Migrating legacy data
Migrating legacy dataMigrating legacy data
Migrating legacy data
 
Tml for Laravel
Tml for LaravelTml for Laravel
Tml for Laravel
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applications
 
Postobjektové programovanie v Ruby
Postobjektové programovanie v RubyPostobjektové programovanie v Ruby
Postobjektové programovanie v Ruby
 
Introducing Elixir and OTP at the Erlang BASH
Introducing Elixir and OTP at the Erlang BASHIntroducing Elixir and OTP at the Erlang BASH
Introducing Elixir and OTP at the Erlang BASH
 
The Ruby On Rails I18n Core Api
The Ruby On Rails I18n Core ApiThe Ruby On Rails I18n Core Api
The Ruby On Rails I18n Core Api
 
I18n
I18nI18n
I18n
 
Zend Certification PHP 5 Sample Questions
Zend Certification PHP 5 Sample QuestionsZend Certification PHP 5 Sample Questions
Zend Certification PHP 5 Sample Questions
 
Drupaljam xl 2019 presentation multilingualism makes better programmers
Drupaljam xl 2019 presentation   multilingualism makes better programmersDrupaljam xl 2019 presentation   multilingualism makes better programmers
Drupaljam xl 2019 presentation multilingualism makes better programmers
 
Type safe embedded domain-specific languages
Type safe embedded domain-specific languagesType safe embedded domain-specific languages
Type safe embedded domain-specific languages
 
Python basic
Python basicPython basic
Python basic
 
DataMapper
DataMapperDataMapper
DataMapper
 
Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript Using CoffeeScript, Backbone.js and JasmineRails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
Rails-like JavaScript Using CoffeeScript, Backbone.js and Jasmine
 
Morel, a data-parallel programming language
Morel, a data-parallel programming languageMorel, a data-parallel programming language
Morel, a data-parallel programming language
 
Lithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksLithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate Frameworks
 

Más de Clinton Dreisbach

Más de Clinton Dreisbach (7)

Migrating Legacy Rails Apps to Rails 3
Migrating Legacy Rails Apps to Rails 3Migrating Legacy Rails Apps to Rails 3
Migrating Legacy Rails Apps to Rails 3
 
Having Fun with Play
Having Fun with PlayHaving Fun with Play
Having Fun with Play
 
Narwhal and the Adventures of CommonJS
Narwhal and the Adventures of CommonJSNarwhal and the Adventures of CommonJS
Narwhal and the Adventures of CommonJS
 
HTML5 Now
HTML5 NowHTML5 Now
HTML5 Now
 
Unearthed Arcana for Web People
Unearthed Arcana for Web PeopleUnearthed Arcana for Web People
Unearthed Arcana for Web People
 
The Joy Of Ruby
The Joy Of RubyThe Joy Of Ruby
The Joy Of Ruby
 
Dealing with Legacy PHP Applications
Dealing with Legacy PHP ApplicationsDealing with Legacy PHP Applications
Dealing with Legacy PHP Applications
 

Último

How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?Igalia
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)wesley chun
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 

Último (20)

How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 

Advanced Internationalization with Rails

  • 1. Advanced Internationalization with Rails Clinton R. Nixon Viget Labs
  • 2. Hola! Qué tal? Me llamo Clinton R. Nixon. Soy programador de software. Vivo en los Estados Unidos. Mi cuidad es de Durham en Carolina del Norte. Hablo un poco de español. Entiendo un poco mas. Me encanta Ruby y Rails.
  • 3. The current state of Rails i18n Key-value lookup ActiveRecord support Interpolation Weak pluralization Localized formats Localized views (index.es.html.erb)
  • 4. Key-value lookup en:    site_title:  The  Lost  Tourist    add_tourism_office:  Add  a  New  Tourism  Office    add_city:  Add  a  New  City es:    site_title:  El  Turista  que  se  Pierde    add_tourism_office:  Crear  una  Nueva  Oficina  de  Turismo    add_city:  Crear  una  Nueva  Cuidad link_to(t(:site_title),  root_url)
  • 5. ActiveRecord support es:    activerecord:        errors:            messages:                phone_number:  "no  se  puede  en  blanco  a  menos  que  el  número  de   teléfono  de  trabajo,  número  de  teléfono  móvil,  o  otro  número  de  teléfono"        models:            business:  Negocio            business_plan:  Plan  de  Negocios            business_process:  Función  Crítica        attributes:            pet:                name:  Nombre                species:  Especies                special_needs:  Este  Mascota  Tiene  Necesidades  Especiales  Médicas
  • 6. Interpolation es:    welcome_to:  "Bienvenido  a  {{city}}!" t(:welcome_to,  :city  =>  @city.name)
  • 7. Pluralization es:    city_has_x_tourism_offices:          one:  "Hay  {{count}}  oficina  de  turismo  de  {{city}}."        other:  "Hay  {{count}}  oficinas  de  turismo  de  {{city}}." t(:city_has_x_tourism_offices,  :city  =>   @city.name,  :count  =>  @city.tourism_offices.count)
  • 8. Localized formats es:    number:        format:            #  Sets  the  separator  between  the  units  (e.g.  1.0  /  2.0  ==  0.5)            separator:  ","              #  Delimits  thousands  (e.g.  1,000,000  is  a  million)            delimiter:  "."              #  Number  of  decimals  (1  with  a  precision  of  2  gives:  1.00)            precision:  3        #  Used  in  number_to_currency()        currency:            format:                #  %u  is  the  currency  unit,  %n  the  number  (default:  $5.00)                format:  "%n  %u"                  unit:  "€"                  #  These  three  are  to  override  number.format  and  are  optional                separator:  ","                  delimiter:  "."                  precision:  2
  • 9. Localized views app/    views/        cities/            show.haml        #  used  by  default  locale            show.es.haml  #  used  by  :es  locale
  • 10. Difficulties File-based key-value lookup is not user-editable. Model attributes are not translated. The Rails inflector is not multi-lingual.
  • 11. Plugins to help Newsdesk Translate translatable_columns Globalize2
  • 12. Model attribute translations test  "returns  the  value  for  the  correct  locale"  do    post  =  Post.create  :subject  =>  'foo'    I18n.locale  =  'de'    post.subject  =  'bar'    post.save    I18n.locale  =  'en'    post  =  Post.first    assert_equal  'foo',  post.subject    I18n.locale  =  'de'    assert_equal  'bar',  post.subject end
  • 13. Other Globalize2 features Locale fallbacks    I18n.fallbacks[:"es-­‐MX"]  #  =>  [:"es-­‐MX",  :es,  :"en-­‐US",  :en] Custom pluralization    @backend.add_pluralizer  :cz,  lambda{|c|          c  ==  1  ?  :one  :  (2..4).include?(c)  ?  :few  :  :other      } Metadata    rails  =  I18n.t  :rails    #  if  no  translation  can  be  found:    rails.locale                      #  =>  :en    rails.requested_locale  #  =>  :es      rails.fallback?                #  =>  true    rails.options                    #  returns  the  options  passed  to  #t    rails.plural_key              #  returns  the  plural_key  (e.g.  :one,  :other)    rails.original                  #  returns  the  original  translation
  • 14. Translating models Globalize2 and translatable_models work well for this.
  • 15. How to start building a backend The easiest way is to extend I18n::Backend::Simple. Key methods are: init_translations  (protected) store_translations translate localize available_locales
  • 16. Example: DB-backed backend class  SnippetBackend  <  I18n::Backend::Simple    #  These  are  the  only  supported  locales    LOCALES  =  [:en,  :es]          protected    def  init_translations        load_translations(*I18n.load_path.flatten)        load_translations_from_database        @initialized  =  true    end    #  ... end
  • 17. Example: DB-backed backend class  SnippetBackend  <  I18n::Backend::Simple    #  These  are  the  only  supported  locales    LOCALES  =  [:en,  :es]          protected    def  init_translations        load_translations(*I18n.load_path.flatten)        load_translations_from_database        @initialized  =  true    end    #  ... end
  • 18. Storing the translations class  Snippet  <  ActiveRecord::Base    validates_presence_of  :name,  :en_text,  :es_text    validates_uniqueness_of  :name    validate  :name_must_not_be_subset_of_other_name    private    def  name_must_not_be_subset_of_other_name        if  self.name  =~  /./            snippets  =  Snippet.find(:all,                      :conditions  =>  ['name  LIKE  ?',  "#{self.name}%"])            snippets.reject!  {  |s|  s.id  ==  self.id  }  unless  self.new_record?            unless  snippets.empty?                errors.add(:name,                      "must  not  be  a  subset  of  another  snippet  name")            end        end    end end
  • 19. Loading translations from DB def  load_translations_from_database    data  =  {  }    LOCALES.each  {  |locale|  data[locale]  =  {}  }    Snippet.all.each  do  |snippet|        path  =  snippet.name.split(".")        key  =  path.pop        current  =  {}        LOCALES.each  {  |locale|  current[locale]  =  data[locale]  }        path.each  do  |group|            LOCALES.each  do  |locale|                  current[locale][group]  ||=  {}                current[locale]  =  current[locale][group]            end        end        LOCALES.each  {  |locale|              current[locale][key]  =  snippet.read_attribute("#{locale}_text")  }                    end    data.each  {  |locale,  d|  merge_translations(locale,  d)  } end
  • 20. Example: a bad idea module  Traductor    class  GoogleBackend  <   I18n::Backend::Simple        def  translate(locale,  key,  options  =   {})            if  locale.to_s  ==  'en'                key            else                Translate.t(key,  'en',  locale.to_s)            end        end    end
  • 21. Translation data format {:en  =>  {      "pets"  =>  {  "canine"  =>  "dog",  "feline"  =>  "cat"  },    "greeting"  =>  "Hello!",    "farewell"  =>  "Goodbye!"  },  :es  =>  {      "pets"  =>  {  "canine"  =>  "perro",  "feline"  =>   "gato"  },      "greeting"  =>  "¡Hola!",      "farewell"  =>  "¡Hasta  luego!"  }  } One could override merge_translations if you needed to bring in data in another format.
  • 22. Improving inflections Let’s make the Rails inflector multilingual! This will be incredibly invasive, full of monkey- patching, and quite dangerous.
  • 23. Why? Generated forms: Formtastic, Typus %h2=  City.human_name.pluralize
  • 24. Black magic module  ActiveSupport::Inflector    def  inflections(locale  =  nil)        locale  ||=  I18n.locale        locale_class  =  ActiveSupport::Inflector.const_get(            "Inflections_#{locale}")  rescue  nil        if  locale_class.nil?            ActiveSupport::Inflector.module_eval  %{                class  ActiveSupport::Inflector::Inflections_#{locale}  <   ActiveSupport::Inflector::Inflections                end            }            locale_class  =  ActiveSupport::Inflector.const_get(                "Inflections_#{locale}")        end        block_given?  ?  yield  locale_class.instance  :  locale_class.instance    end
  • 25. Black magic module  ActiveSupport::Inflector    def  inflections(locale  =  nil)        locale  ||=  I18n.locale        locale_class  =  ActiveSupport::Inflector.const_get(            "Inflections_#{locale}")  rescue  nil        if  locale_class.nil?            ActiveSupport::Inflector.module_eval  %{                class  ActiveSupport::Inflector::Inflections_#{locale}  <   ActiveSupport::Inflector::Inflections                end            }            locale_class  =  ActiveSupport::Inflector.const_get(                "Inflections_#{locale}")        end        block_given?  ?  yield  locale_class.instance  :  locale_class.instance    end
  • 26. Black magic module  ActiveSupport::Inflector    def  inflections(locale  =  nil)        locale  ||=  I18n.locale        locale_class  =  ActiveSupport::Inflector.const_get(            "Inflections_#{locale}")  rescue  nil        if  locale_class.nil?            ActiveSupport::Inflector.module_eval  %{                class  ActiveSupport::Inflector::Inflections_#{locale}  <   ActiveSupport::Inflector::Inflections                end            }            locale_class  =  ActiveSupport::Inflector.const_get(                "Inflections_#{locale}")        end        block_given?  ?  yield  locale_class.instance  :  locale_class.instance    end
  • 27. Success! ActiveSupport::Inflector.inflections(:es)  do  |inflect|    inflect.plural  /$/,  's'    inflect.plural  /([^aeioué])$/,  '1es'    inflect.plural  /([aeiou]s)$/,  '1'    inflect.plural  /z$/,  'ces'    inflect.plural  /á([sn])$/,  'a1es'    inflect.plural  /í([sn])$/,  'i1es'    inflect.plural  /ó([sn])$/,  'o1es'    inflect.plural  /ú([sn])$/,  'u1es'    inflect.singular  /s$/,  ''    inflect.singular  /es$/,  ''    inflect.irregular('papá',  'papás')    inflect.irregular('mamá',  'mamás')    inflect.irregular('sofá',  'sofás') end
  • 28. A minor problem "oficina  de  turismo".pluralize   #  =>  "oficina  de  turismos" "contacto  de  emergencia".pluralize #  =>  "contacto  de  emergencias"
  • 29. Lambdas to the rescue def  pluralize(word,  locale  =  nil)    locale  ||=  I18n.locale    result  =  word.to_s.dup    #  ...  elided  ...    inflections(locale).plurals.each  do  |(rule,  replacement)|        if  replacement.respond_to?(:call)            break  if  result.gsub!(rule,  &replacement)        else            break  if  result.gsub!(rule,  replacement)        end    end    result end
  • 30. Lambdas to the rescue ActiveSupport::Inflector.inflections(:es)  do  |inflect|    #  ...  elided  ...    inflect.plural(/^(w+)s(.+)$/,  lambda  {  |match|                                      head,  tail  =  match.split(/s+/,  2)                                      "#{head.pluralize}  #{tail}"                                  })    #  ...  elided  ...     end
  • 31. Success! I18n.locale  =  :es "oficina  de  turismo".pluralize   #  =>  "oficinas  de  turismo" "contacto  de  emergencia".pluralize #  =>  "contactos  de  emergencia" I18n.locale  =  :en "emergency  contact".pluralize #  =>  "emergency  contacts"
  • 32. The inflector is used everywhere Because of the wide-spread use of the inflector, we have to patch: ActiveSupport::ModelName#initialize String#pluralize,  singularize AS::Inflector#pluralize,  singularize,  tableize ActiveRecord::Base.human_name,  undecorated_table_name AR::Reflection::AssociationReflection#derive_class_name ActionController::PolymorphicRoutes#build_named_route_call
  • 33. An interesting side-effect ActiveRecord::Schema.define  do    create_table  "oficinas_de_turismo"  do  |t|        t.string  "nombre"        t.string  "calle"    end end context  "En  español"  do    setup  {  I18n.default_locale  =  :es  }    context  "OficinaDeTurismo"  do        should("use  the  right  table")  {              OficinaDeTurismo.table_name  }.equals  "oficinas_de_turismo"    end end
  • 34. Muchas gracias a todos! crnixon@gmail.com http://crnixon.org http://github.com/crnixon/traductor http://delicious.com/crnixon/conferencia.rails+i18n