This article is about the process of setting up Rails to use localization to brazilian-portuguese (pt-br), i.e., have ActiveRecord error messsages, hours and dates to brazilian-pt; and to configures the development to use brazilian-pt, i.e., use Rails pluralization/singularization so that models, controllers and views can be creates in pt-br.

Changing the interface to brazilian-pt

Create the file config/locales/pt-BR.yml and insert the following:

pt-BR:
  # formatos de data e hora
  date:
    formats:
      default: "%d/%m/%Y"
      short: "%d de %B"
      long: "%d de %B de %Y"

    day_names: [Domingo, Segunda, Terça, Quarta, Quinta, Sexta, Sábado]
    abbr_day_names: [Dom, Seg, Ter, Qua, Qui, Sex, Sáb]
    month_names: [~, Janeiro, Fevereiro, Março, Abril, Maio, Junho, Julho, Agosto, Setembro, Outubro, Novembro, Dezembro]
    abbr_month_names: [~, Jan, Fev, Mar, Abr, Mai, Jun, Jul, Ago, Set, Out, Nov, Dez]
    order: [day, month, year]

  time:
    formats:
      default: "%A, %d de %B de %Y, %H:%M h"
      short: "%d/%m, %H:%M h"
      long: "%A, %d de %B de %Y, %H:%M h"
    am: ''
    pm: ''

  # distancia do tempo em palavras
  datetime:
    distance_in_words:
      half_a_minute: 'meio minuto'
      less_than_x_seconds:
        one: 'menos de 1 segundo'
        other: 'menos de %{count} segundos'

      x_seconds:
        one: '1 segundo'
        other: '%{count} segundos'

      less_than_x_minutes:
        one: 'menos de um minuto'
        other: 'menos de %{count} minutos'

      x_minutes:
        one: '1 minuto'
        other: '%{count} minutos'

      about_x_hours:
        one: 'aproximadamente 1 hora'
        other: 'aproximadamente %{count} horas'

      x_days:
        one: '1 dia'
        other: '%{count} dias'

      about_x_months:
        one: 'aproximadamente 1 mês'
        other: 'aproximadamente %{count} meses'

      x_months:
        one: '1 mês'
        other: '%{count} meses'

      about_x_years:
        one: 'aproximadamente 1 ano'
        other: 'aproximadamente %{count} anos'

      over_x_years:
        one: 'mais de 1 ano'
        other: 'mais de %{count} anos'

      almost_x_years:
        one: 'quase 1 ano'
        other: 'quase %{count} anos'

    prompts:
      year:   "Ano"
      month:  "Mês"
      day:    "Dia"
      hour:   "Hora"
      minute: "Minuto"
      second: "Segundos"

  # numeros
  number:
    format:
      precision: 3
      separator: ','
      delimiter: '.'
    currency:
      format:
        unit: 'R$'
        precision: 2
        format: '%u %n'
        separator: ','
        delimiter: '.'
    percentage:
      format:
        delimiter: '.'
    precision:
      format:
        delimiter: '.'
    human:
      format:
        precision: 2
        delimiter: '.'
        significant: true
        strip_unsignificant_zeros: true
      # number_to_human_size()
      storage_units:
        format: "%n %u"
        units:
          byte:
            one: "Byte"
            other: "Bytes"
          kb: "KB"
          mb: "MB"
          gb: "GB"
          tb: "TB"
      # number_to_human()
      # new in rails 3: please add to other locales
      decimal_units:
        format: "%n %u"
        units:
          unit: ""     
          thousand: "mil"
          million:
            one: milhão
            other: milhões
          billion:
            one: bilhão
            other: bilhões
          trillion:
            one: trilhão
            other: trilhões
          quadrillion:
            one: quatrilhão
            other: quatrilhões

  # Usado no Array.to_sentence
  support:
    array:
      words_connector: ", "
      two_words_connector: " e "
      last_word_connector: " e "

  # ActiveRecord
  activerecord:
    errors:
      template:
        header:
          one: "Não foi possível gravar %{model}: 1 erro"
          other: "Não foi possível gravar %{model}: %{count} erros."
        body: "Por favor, verifique o(s) seguinte(s) campo(s):"
      messages:
        inclusion: "não está incluído na lista"
        exclusion: "não está disponível"
        invalid: "não é válido"
        confirmation: "não está de acordo com a confirmação"
        accepted: "deve ser aceito"
        empty: "não pode ficar vazio"
        blank: "não pode ficar em branco"
        too_long: "é muito longo (máximo: %{count} caracteres)"
        too_short: "é muito curto (mínimo: %{count} caracteres)"
        wrong_length: "não possui o tamanho esperado (%{count} caracteres)"
        taken: "já está em uso"
        not_a_number: "não é um número"
        not_an_integer: "não é um número inteiro"
        greater_than: "deve ser maior do que %{count}"
        greater_than_or_equal_to: "deve ser maior ou igual a %{count}"
        equal_to: "deve ser igual a %{count}"
        less_than: "deve ser menor do que %{count}"
        less_than_or_equal_to: "deve ser menor ou igual a %{count}"
        odd: "deve ser ímpar"
        even: "deve ser par"
        record_invalid: "A validação falhou: %{errors}"

Making brazilian-pt the default language to your application

Go to config/application.rb and add the following:

#config/application.rb
config.i18n.default_locale = :"pt-BR"
I18n.enforce_available_locales = false

Changing pluralization to brazilian-pt

The file config/initializers/inflections.rb has the responsibility to tell Rails how to pluralize or sigularize names. Add the following:

# encoding: utf-8
# Be sure to restart your server when you modify this file.

# Add new inflection rules using the following format
# (all these examples are active by default):
# ActiveSupport::Inflector.inflections do |inflect|
#   inflect.plural /^(ox)$/i, '\1en'
#   inflect.singular /^(ox)en/i, '\1'
#   inflect.irregular 'person', 'people'
#   inflect.uncountable %w( fish sheep )
# end
ActiveSupport::Inflector.inflections do |inflect|
  inflect.clear

  inflect.plural(/$/,  's')
  inflect.plural(/(s)$/i,  '\1')
  inflect.plural(/^(paí)s$/i, '\1ses')
  inflect.plural(/(z|r)$/i, '\1es')
  inflect.plural(/al$/i,  'ais')
  inflect.plural(/el$/i,  'eis')
  inflect.plural(/ol$/i,  'ois')
  inflect.plural(/ul$/i,  'uis')
  inflect.plural(/([^aeou])il$/i,  '\1is')
  inflect.plural(/m$/i,   'ns')
  inflect.plural(/^(japon|escoc|ingl|dinamarqu|fregu|portugu)ês$/i,  '\1eses')
  inflect.plural(/^(|g)ás$/i,  '\1ases')
  inflect.plural(/ão$/i,  'ões')
  inflect.plural(/^(irm|m)ão$/i,  '\1ãos')
  inflect.plural(/^(alem|c|p)ão$/i,  '\1ães')

  # Sem acentos...
  inflect.plural(/ao$/i,  'oes')
  inflect.plural(/^(irm|m)ao$/i,  '\1aos')
  inflect.plural(/^(alem|c|p)ao$/i,  '\1aes')

  inflect.singular(/([^ê])s$/i, '\1')
  inflect.singular(/^(á|gá|paí)s$/i, '\1s')
  inflect.singular(/(r|z)es$/i, '\1')
  inflect.singular(/([^p])ais$/i, '\1al')
  inflect.singular(/eis$/i, 'el')
  inflect.singular(/ois$/i, 'ol')
  inflect.singular(/uis$/i, 'ul')
  inflect.singular(/(r|t|f|v)is$/i, '\1il')
  inflect.singular(/ns$/i, 'm')
  inflect.singular(/sses$/i, 'sse')
  inflect.singular(/^(.*[^s]s)es$/i, '\1')
  inflect.singular(/ães$/i, 'ão')
  inflect.singular(/aes$/i, 'ao')
  inflect.singular(/ãos$/i, 'ão')    
  inflect.singular(/aos$/i, 'ao')
  inflect.singular(/ões$/i, 'ão')
  inflect.singular(/oes$/i, 'ao')
  inflect.singular(/(japon|escoc|ingl|dinamarqu|fregu|portugu)eses$/i, '\1ês')
  inflect.singular(/^(g|)ases$/i,  '\1ás')

  # Incontáveis
  inflect.uncountable %w( tórax tênis ônibus lápis fênix )

  # Irregulares
  inflect.irregular "país", "países"
end

Pluralization for non regular words/names

When creating models, controllers or views in brazilian-pt, it is a good practice to see how Rails will pluralize it, to prevent the case of non regular names.
Actually that happens in any language, even english.
Open Rails console to check it:

$ rails c

For example checkin for the word “Usuario”, type the following

2.3.1 :001 > "usuario".pluralize

the result should be

=> "usuarios"

then type

2.3.1 :002 > "usuarios".singularize

the result should be

 => "usuario"

To avoid problems and force Rails to make the correct pluralization/singularization edit the inflections.rb file like this:

inflect.irregular "usuario", "usuarios"

Creating models and controllers with more than one word

Rails pluralization was designed with the English language in mind, where adjectives are placed before nouns, so it will pluralize only the last word of the name.
If you want to create a model to store student grades like “NotaDoAluno”, Rails will generate the plural to “NotaDoAlunos”, which is not really what is wanted.
To avoid that it is better than crete the model as “AlunoNota”, which Rails will pluralize to “AlunoNotas”, which is preferable. The same applies to controllers.

You can also verrule the naming conventions of Rails models, and manually configure the table name of that particular model:

class Product < ActiveRecord::Base
  self.table_name = "notas_do_aluno"
end

Many-to-many tables

When you need to create a many-to-many relationship, it is common to have the names of the two tables in the plural, to do that edit the inflections.rb file, so that when executing rails generate, it will create models and controllers with both words in plural.

inflect.irregular "postagemtag", "postagenstags"
inflect.irregular "postagem tag", "postagens tags"
inflect.irregular "postagem_tag", "postagens_tags"

Changing routes to portuguese

Edite the routes.rb file:

resources :usuarios, path_names: { new: "novo", edit: "editar" }

Or using scopes:

scope path_names: { new: "novo", edit: "editar" } do
  # seus resources

end

The path variables are not changed, they are kept as “new” and “edit”, for example “new_usuario_path” e “edit_usuario_path(@usuario)” .

Configuring timezone

By default Rails shows and store time int the databse using UTC.

If you want that Rails keep on storing time with UTC format, but convert it to the user in another timezone, like Rio de Janeiro, use the following on config/application.rb:

config.time_zone = "Brasilia"

The is the best if you need to support users from different countries/timezones

However, if you wish that Rails stores the timezone like it shows to the user, use:

config.time_zone = "Brasilia"
config.active_record.default_timezone = :local

That is better when you are using a lagacy database, that uses Brasília timezone, or if you import data from another database directly via SQL and not via ActiveRecord.

Reference

http://vaidehijoshi.github.io/blog/2015/09/01/inflections-everywhere-using-activesupport-inflector/

https://pt.stackoverflow.com/questions/19512/como-programar-em-português-no-ruby-on-rails