Rails 5.1 Consulting und Schulung vom Autor:
www.wintermeyer-consulting.de/rails/

10.2. Eine einsprachig deutsche Rails-Applikation

Es reicht bei einer nur für deutsche User ausgerichteten Rails-Applikation leider nicht aus, einfach alle Views ins Deutsche zu übersetzen. Die Vorgehensweise ähnelt in vielen Punkten der einer mehrsprachigen Rails-Applikation (siehe Abschnitt 10.3, „Mehrsprachige Rails-Applikation“). Entsprechend wird es einige Wiederholungen geben. Anhand einer einfachen Applikation zeige ich Ihnen, welche Schritte beachtet werden müssen. Gehen wir alle diese Änderung an folgender Literaturverzeichnis-Applikation durch:
MacBook:~ xyz$ rails new bibliography
[...]
MacBook:~ xyz$ cd bibliography 
MacBook:bibliography xyz$ rails generate scaffold book title number_of_pages:integer 'price:decimal{7,2}'
[...]
MacBook:bibliography xyz$ rake db:migrate
[...]
MacBook:bibliography xyz$
Um Beispiele für Validierungsfehler zu bekommen, fügen Sie bitte in der app/models/book.rb folgende Validierungen ein:
class Book < ActiveRecord::Base
  attr_accessible :number_of_pages, :price, :title

  validates :title,
            :presence => true,
            :uniqueness => true,
            :length => { :within => 2..255 }

  validates :price,
            :presence => true,
            :numericality => { :greater_than => 0 }
end
Bitte suchen Sie in der Konfigurations-Datei config/application.rb nach dem Wert config.i18n.default_locale und setzen Sie ihn auf :de für deutsch. Im gleichen Zusammenhang fügen wir dann auch in der Zeile darüber noch zwei weitere Verzeichnisse für Übersetzungen der Models und der Views ein. Diese Verzeichnisstruktur ist nicht technisch notwendig, verschafft aber bei größeren Applikationen einen besseren Überblick:
    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', 'models', '*', '*.yml').to_s]
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', 'views', '*', '*.yml').to_s]
    config.i18n.default_locale = :de
Die entsprechenden Verzeichnisse müssen dann noch angelegt werden:
MacBook:bibliography xyz$ mkdir -p config/locales/models/book
MacBook:bibliography xyz$ mkdir -p config/locales/views/book
MacBook:bibliography xyz$
Jetzt müssen Sie eine Sprachkonfigurationsdatei für Deutsch generieren oder einfacher eine fertige von Sven Fuchs aus seinem Github-Repository https://github.com/svenfuchs/rails-i18n downloaden:
MacBook:bibliography xyz$ cd config/locales
MacBook:locales xyz$ curl -O https://raw.github.com/svenfuchs/rails-i18n/master/rails/locale/de.yml
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  4940  100  4940    0     0   9574      0 --:--:-- --:--:-- --:--:-- 11932
MacBook:locales xyz$ 

Anmerkung

Wenn Sie wissen, wie Bundler funktioniert, können Sie auch die Zeile gem 'rails-i18n' in die Datei Gemfile einfügen und danach bundle ausführen. Damit bekommen Sie alle Sprachdateien aus dem Repository eingespielt.
In der config/locales/de.yml-Datei sind die benötigten Formate und generischen Formulierungen für Deutsch enthalten, die wir für eine normale Rails-Applikation benötigen (z. B. die Wochentage, Währungssymbol usw.). Schauen Sie einmal mit Ihrem Lieblingseditor rein, um einen Überblick zu bekommen.
Als Nächstes müssen wir Rails erklären, dass in Deutsch ein Model 'book' nicht 'book', sondern 'Buch' heißt. Das Gleiche gilt für alle Attribute. Dafür legen wir die Datei config/locales/models/book/de.yml mit folgender Struktur an. Als Nebeneffekt bekommen wir die Methoden Model.model_name.human und Model.human_attribute_name(attribute), mit denen wir die Model- und Attributnamen im View einfügen können.
# ruby encoding: utf-8

de:
  activerecord:
    models:
      book: 'Buch'
    attributes:
      book:
        title: 'Titel'
        number_of_pages: 'Seitenanzahl'
        price: 'Preis'
In der Datei config/locales/views/book/de.yml fügen wir ein paar Werte für die Scaffold Views ein:
# ruby encoding: utf-8

de:
  views:
    show: Anzeigen
    edit: Editieren
    destroy: Löschen
    are_you_sure: Sind Sie sicher?
    back: Zurück
    edit: Editieren
    book:
      index:
        title: Bücherliste
        new: Neues Buch
      edit:
        title: Buch editieren
      new:
        title: Neues Buch
      flash_messages:
        book_was_successfully_created: 'Das Buch wurde erfolgreich angelegt.'
        book_was_successfully_updated: 'Das Buch wurde erfolgreich aktualisiert.'
Jetzt müssen wir noch "ein paar" Veränderungen in den Views einbauen. Wir benutzen dabei den I18n.t Helper, der im View auch mit t abgekürzt werden kann. I18n.t ließt aus der YAML-Datei den entsprechenden Eintrag aus. Wir könnten bei einer rein deutschsprachigen Applikation auch direkt den deutschen Text in den View schreiben, aber so können wir im Fall des Falles leichter auf Mehrsprachigkeit umstellen. Mehr Informationen zu I18n.t finden Sie unter „I18n.t“.
app/views/books/_form.html.erb
<%= form_for(@book) do |f| %>
  <% if @book.errors.any? %>
    <div id="error_explanation">
      <h2><%= t 'activerecord.errors.template.header', :model => Book.model_name.human, :count => @book.errors.count %></h2>
      <ul>
      <% @book.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :title %><br />
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :number_of_pages %><br />
    <%= f.number_field :number_of_pages %>
  </div>
  <div class="field">
    <%= f.label :price %><br />
    <%= f.text_field :price %>
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>
app/views/books/edit.html.erb
<h1><%= t 'views.book.edit.title' %></h1>

<%= render 'form' %>

<%= link_to I18n.t('views.show'), @book %> |
<%= link_to I18n.t('views.back'), books_path %>
app/views/books/index.html.erb
<h1><%= t 'views.book.index.title' %></h1>

<table>
  <tr>
    <th><%= Book.human_attribute_name(:title) %></th>
    <th><%= Book.human_attribute_name(:number_of_pages) %></th>
    <th><%= Book.human_attribute_name(:price) %></th>
    <th></th>
    <th></th>
    <th></th>
  </tr>

<% @books.each do |book| %>
  <tr>
    <td><%= book.title %></td>
    <td><%= number_with_delimiter(book.number_of_pages) %></td>
    <td><%= number_to_currency(book.price) %></td>
    <td><%= link_to I18n.t('views.show'), book %></td>
    <td><%= link_to I18n.t('views.edit'), edit_book_path(book) %></td>
    <td><%= link_to I18n.t('views.destroy'), book, confirm: I18n.t('views.are_you_sure'), method: :delete %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to I18n.t('views.book.index.new'), new_book_path %>
app/views/books/new.html.erb
<h1><%= t 'views.book.new.title' %></h1>

<%= render 'form' %>

<%= link_to I18n.t('views.back'), books_path %>
app/views/books/show.html.erb
<p id="notice"><%= notice %></p>

<p>
  <b><%= Book.human_attribute_name(:title) %>:</b>
  <%= @book.title %>
</p>

<p>
  <b><%= Book.human_attribute_name(:number_of_pages) %>:</b>
  <%= number_with_delimiter(@book.number_of_pages) %>
</p>

<p>
  <b><%= Book.human_attribute_name(:price) %>:</b>
  <%= number_to_currency(@book.price) %>
</p>


<%= link_to I18n.t('views.edit'), edit_book_path(@book) %> |
<%= link_to I18n.t('views.back'), books_path %>

Anmerkung

Im show- und index-View habe ich die Helper number_with_delimiter und number_to_currency eingebaut, damit die Zahlen für den End-User schöner dargestellt werden.
Ganz zum Schluss müssen wir im Controller app/controllers/books_controller.rb noch ein paar Flash-Messages anpassen:
class BooksController < ApplicationController
  # GET /books
  # GET /books.json
  def index
    @books = Book.all

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @books }
    end
  end

  # GET /books/1
  # GET /books/1.json
  def show
    @book = Book.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @book }
    end
  end

  # GET /books/new
  # GET /books/new.json
  def new
    @book = Book.new

    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @book }
    end
  end

  # GET /books/1/edit
  def edit
    @book = Book.find(params[:id])
  end

  # POST /books
  # POST /books.json
  def create
    @book = Book.new(params[:book])

    respond_to do |format|
      if @book.save
        format.html { redirect_to @book, notice: I18n.t('views.book.flash_messages.book_was_successfully_created') }
        format.json { render json: @book, status: :created, location: @book }
      else
        format.html { render action: "new" }
        format.json { render json: @book.errors, status: :unprocessable_entity }
      end
    end
  end

  # PUT /books/1
  # PUT /books/1.json
  def update
    @book = Book.find(params[:id])

    respond_to do |format|
      if @book.update_attributes(params[:book])
        format.html { redirect_to @book, notice: I18n.t('views.book.flash_messages.book_was_successfully_updated') }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @book.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /books/1
  # DELETE /books/1.json
  def destroy
    @book = Book.find(params[:id])
    @book.destroy

    respond_to do |format|
      format.html { redirect_to books_url }
      format.json { head :no_content }
    end
  end
end
Jetzt können Sie die vom Scaffold Generator angelegten Views komplett auf Deutsch benutzen. Die hier gezeigte Struktur der YAML-Dateien lässt sich natürlich nach eigenem Geschmack anpassen. Die Texte in den Views und im Controller werden mit I18n.t angezeigt. An dieser Stelle könnte man natürlich bei einer rein deutschen Anwendung auch direkt den deutschen Text einbauen.

Deutschsprachige Pfade

Unsere Bücherliste ist komplett deutsch, aber die aufzurufenden URLs sind alle noch in Englisch. Wenn wir alle Bücher unter der URL http://0.0.0.0:3000/buecher und nicht unter http://0.0.0.0:3000/books abrufbar machen möchten, so müssen wir in der config/routes.rb folgenden Eintrag machen:
Bibliography::Application.routes.draw do

  scope(:path_names => { :new => "neu", :edit => "bearbeiten" }) do
    resources :books, :path => "buecher"
  end

end
Damit ergeben sich folgende neue Pfade:
MacBook:bibliography xyz$ rake routes
    books GET    /buecher(.:format)                books#index
          POST   /buecher(.:format)                books#create
 new_book GET    /buecher/neu(.:format)            books#new
edit_book GET    /buecher/:id/bearbeiten(.:format) books#edit
     book GET    /buecher/:id(.:format)            books#show
          PUT    /buecher/:id(.:format)            books#update
          DELETE /buecher/:id(.:format)            books#destroy
MacBook:bibliography xyz$
Das Schöne an den Rails-Routen ist, dass Sie nicht mehr machen müssen. Der Rest wird transparent von den Routen verwaltet.

Autor

Stefan Wintermeyer