[WB//Rails4]

Fortunka i18n & l10n

Kod aplikacji „Fortunka i18n + l10n”:

Co oznacza i18n, a co l10n?

Wszystkie komunikaty Rails można znaleźć w plikach:

Polskie tłumaczenia wszystkich tych komunikatów są w jednym pliku:

Końcówki liczby mnogiej (pluralization), pl.rb:

O jeden przypadek za dużo? W języku polskim mamy trzy przypadki, np. bajt, bajty i bajtów. Przypadek other czasami pozwala w plikach locales zastąpić trzy tłumaczenia dwoma. Na przykład:

one:   ! '%{model} nie został zachowany z powodu jednego błędu'
other: ! '%{model} nie został zachowany z powodu %{count} błędów'

Bez other musimy wstawić te trzy linijki:

one:  ! '%{model} nie został zachowany z powodu jednego błędu'
few:  ! '%{model} nie został zachowany z powodu %{count} błędów'
many: ! '%{model} nie został zachowany z powodu %{count} błędów'

Metody upacse i downcase

Metody upcasedowncase języka Ruby v1.9.3 nie radzą sobie z polskimi diakrytykami:

"ą".upcase    # => ą
"Ą".downcase  # => Ą

Można temu zaradzić instalując gem Aleksandra Pohla string_case_pl:

Gemfile
gem 'string_case_pl'

Teraz na konsoli Rails:

"ą".upcase    # => Ą
"Ą".downcase  # => ą

Gemy z wsparciem dla I18N

Przechodzimy na język polski

1. Zmieniamy locale edytując wiersz z config.i18n.default_locale w pliku application.rb:

config/application.rb
# The default locale is :en and all translations
# from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
config.i18n.default_locale = :pl

2. Wersję raw pliku pl.yml zapisujemy w katalogu config/locales/.

3. Angielskie nazwy modeli i ich atrybutów też można przetłumaczyć. Przykład pokazujący jak to zrobić znajdziemy w pliku active_record.yml.

Więcej informacji na ten temat znajdziemy w przewodniku Translations for Active Record Models.

Pluralization, czyli liczba mnoga

Definicję modułu RailsI18n::Pluralization::Polish z pliku pl.rb zapisujemy w config/initializers/pluralization.rb. Następnie na początku pliku dopisujemy wiersz zaczynający się od I18n::Backend::Simple:

config/initializers/pluralization.rb
I18n::Backend::Simple.send(:include, I18n::Backend::Pluralization)

module RailsI18n
  module Pluralization
    module Polish
      def self.rule
        lambda do |n|
          if n == 1
            :one
          elsif [2, 3, 4].include?(n % 10) && ![12, 13, 14].include?(n % 100)
            :few
          elsif ([0, 1] + (5..9).to_a).include?(n % 10) || [12, 13, 14].include?(n % 100)
            :many
          else
            :other
          end
        end
      end end end end

W katalogu config/locales dodajemy plik pl.rb i wklejamy do niego pozostały kod z pl.rb:

config/locales/pl.rb
{ :pl => {
    :'i18n' => {
      :plural => {
        :keys => [:one, :few, :many, :other],
        :rule => RailsI18n::Pluralization::Polish.rule } } } }

Po dodaniu walidacji do modelu Fortunes:

app/models/fortunes.rb
validates :quotation, :presence => true, :length => { :maximum => 100 }
validates :source, :length => { :minimum => 2, :maximum => 40 }

i próbie utworzenia nowej fortunki, widzimy że komunikat

jest za krótkie (przynajmniej 2 znaków)

został niepoprawnie przetłumaczony. Poprawne tłumaczenie to:

jest za krótkie (przynajmniej 2 znaki)

Poprawiamy oba tłumaczenie too_short oraz to_long:

config/locales/pl.yml
too_long:
  one:  "jest za długie (maksymalnie %{count} znak)"
  few:  "jest za długie (maksymalnie %{count} znaki)"
  many: "jest za długie (maksymalnie %{count} znaków)"
too_short:
  one:  "jest za krótkie (minimalnie %{count} znak)"
  few:  "jest za krótkie (minimalnie %{count} znaki)"
  many: "jest za krótkie (minimalnie %{count} znaków)"

U góry strony widzimy nieprzetłumaczony komunikat:

Some errors were found, please take a look:

Komunikat ten znajdziemy w pliku simple_form.en.yml:

config/locales/simple_form.en.pl
en:
  simple_form:
    "yes": 'Yes'
    "no": 'No'
    required:
      text: 'required'
      mark: '*'
      # You can uncomment the line below if you need to overwrite the whole required html.
      # When using html, text and mark won't be used.
      # html: '<abbr title="required">*</abbr>'
    error_notification:
      default_message: "Some errors were found, please take a look:"
    # Labels and hints examples
    # labels:
    #   password: 'Password'
    #   user:
    #     new:
    #       email: 'E-mail para efetuar o sign in.'
    #     edit:
    #       email: 'E-mail.'
    # hints:
    #   username: 'User name to sign in.'
    #   password: 'No special characters, please.'

Kopiujemy plik:

cp simple_form.en.yml simple_form.pl.yml

zmieniamy język en: na pl: i tłumaczymy komunikaty.

Język polski w widokach

Zaczniemy od przetłumaczenia nagłówków w widokach.

Zamieniamy tytuł we wszystkich widokach, czyli index, show, edit, new, na:

<% title t ".title" %>

Teraz po wejściu na stronę, na przykład główną, znajdziemy w źródle tej strony taki kod:

<span class="translation_missing" title="translation missing: pl.fortunes.index.title">Title</span>

Oznacza to, że tłumaczenia należy wpisać w taki sposób:

pl:
  fortunes:
    index:
      title: "Wszystkie Fortunki"
en:
  fortunes:
    index:
      title: "All Fortunes"

Polskie tłumaczenia nagłówków w widokach dopiszemy w pl.yml:

pl:
  fortunes:
    index:
      title: "Wszystkie fortunki"
    show:
      title: "Fortunka"
    edit:
      title: "Edycja fortunki"
    new:
      title: "Nowa fortunka"

Nazwy etykiet

Przetłumaczony tekst etykiet (labels) formularza wpiszemy w pliku simple_form.pl.yml:

config/locales/simple_form.pl.yml
pl:
  simple_form:
    "yes": 'Tak'
    "no": 'Nie'
    required:
      text: 'wymagane'
      mark: '*'
      # You can uncomment the line below if you need to overwrite the whole required html.
      # When using html, text and mark won't be used.
      # html: '<abbr title="required">*</abbr>'
    error_notification:
      default_message: "Niepoprawnie wypełniony formularz:"
    labels:
      fortune:
        quotation: 'Cytat'
        source: 'Źródło'

Przy okazji podmieniamy/dodajemy po dwie linijki w widokach index oraz show na:

<span class="name"><%= t "simple_form.labels.fortune.quotation" %></span>
<span class="name"><%= t "simple_form.labels.fortune.source" %></span>

Nazwa modelu (w przycisku Submit)

Wpisujemy w pliku pl.yml poniżej errors:

activerecord:
  errors:
    <<: *errors
  models:
    fortune: "Fortunkę"

Linki: Show, Edit, Destroy, New, Back

Tłumaczenia napisów w linkach.

Oryginał:

<!-- index -->
<%= link_to 'Show' ... %>
<%= link_to 'Edit' ... %>
<%= link_to 'Destroy', fortune, confirm: 'Are you sure?' ... %>
<%= link_to 'New Fortune' ... %>
<!-- show -->
<%= link_to 'Back' ... %>
<%= link_to 'Edit' ... %>
<%= link_to 'Destroy', @fortune, confirm: 'Are you sure?' ... %>

Poprawki:

<%= link_to t('show') ... %>
<%= link_to t('edit') ... %>
<%= link_to t('destroy') ... %>
<%= link_to t('back') ... %>
<%= link_to t('.new') ... %>

Tłumaczenia napisów na przyciskach wpisujemy w taki sposób:

pl:
  fortunes:
    index:
      title: "Wszystkie fortunki"
      new:   "Nowa fortunka"

  ... cut ...

  show: "Pokaż"
  edit: "Edytuj"
  destroy: "Usuń"
  back: "Powrót"

TODO: Skorzystać z attrybutów data-. Tak jak w widokach wygenerowanych przez generatory simple_form i bootstrap:themed.

Alert: Are you sure?

TODO.

W linkach z Destroy zamieniamy:

:confirm => "Are you sure?"

na:

:confirm => "t('confirm')"

Tłumaczenia wpisujemy w plikach: en.ymlpl.yml::

en:
  confirm: "Are you sure?"
pl:
  confirm: "Jesteś pewien, że naprawdę chcesz to zrobić?"

Flash messages

TODO.

Podobnie to przycisków. Tekst komunikatów znajdziemy w kodzie kontrolera:

app/controllers/fortunes_controller.rb
def create
  ...
      format.html { redirect_to @fortune, notice: 'Fortune was successfully created.' }
def update
  ...
      format.html { redirect_to @fortune, notice: 'Fortune was successfully updated.' }

Podmieniamy komunikaty na:

format.html { redirect_to @fortune, notice: t('flash.fortunes.create.notice') }
format.html { redirect_to @fortune, notice: t('flash.fortunes.update.notice') }

W pliku z tłumaczeniem dodajemy:

pl.yml
flash:
  fortunes:
    create:
      notice: "Nową fortunka zapisano w bazie."
    update:
      notice: "Uaktualniono fortunkę."

Eleganckie rozwiązanie i18n dla komunikatów flash znajdziemy w module FlashResponder z gemu Responders.

Responders & flash messages

I18n w Responders:

flash:
  actions:
    create:
      notice: "%{resource_name} was successfully created."
    update:
      notice: "%{resource_name} was successfully updated."
    destroy:
      notice: "%{resource_name} was successfully destroyed."
      alert: "%{resource_name} could not be destroyed."

Przykład pliku z komunikatami tylko dla modelu Fortune:

flash:
  fortunes:
    create:
      notice: "Your post was created and will be published soon"

I18n routing

Czasami musimy tłumaczyć adresy URI. Jak to zrobić opisano w Translate your Rails2/3 routes with ease.