Mehrsprachige Webseiten mit Ruby on Rails (2)

Teil 2 – Platzhalter, Validierungen, Modellklassen

[Zu Teil 1]

Oft enthalten Webseiten Sätze, in denen sich ein einzelnes Wort sich dynamisch ändert. Beispielsweise „Sie sind als „name“ eingeloggt.“ Zur Übersetzung können wir natürlich den Satz aus seinen Bestandteilen zusammenbauen. Oft gibt es ein Problem: in verschiedenen Sprachen ist der Satzbau anders.

Besser ist deshalb die sogenannte Interpolation, wie man sie in Ruby ja kennt. Im Satz steht ein markierter Platzhalter, der zur Laufzeit ersetzt wird. Die yaml-Datei zur Spezifikation nutzt hierbei nicht die Ruby Syntax, sondern markiert die zu ersetzenden durch doppelte geschweifte Klammern. Achtung: In Rails 3 hat sich die Syntax leicht geändert.

# Definition
  logmsg: "You are logged in as {{name}}."
 
# call
I18n.translate(:logmsg, :name => 'Administrator')
# or short form
t :logmsg, :name => 'Administrator'

Wir müssen dabei sicherstellen, das die Parameter von Definition und Aufruf übereinstimmen. Die translate()-Methode dabei relativ tolerant: Überflüssige Parameter werden ignoriert, fehlende Parameter werden mit einem „(???)“ ersetzt.

:!: Achtung: die übersetzende Person muss wissen, dass sie die Variablennamen in den Klammern nicht mit übersetzen soll!
Eine weitere kleine Falle lauert bei den Variablennamen: Sie dürfen nicht wie eine der anderen Parameter der translate()-Methode heißen. Verboten sind deshalb „default“ und „scope“.

Texte der Validierungen

Selbst wenn wir unsere Applikation nur einsprachig auf deutsch erstellen, gibt es an einer Stelle eine Überraschung: bei den automatisch generierten Fehlermeldungen der Validierungen in den Model-Klassen. Die sind sehr praktisch, aber dummerweise in Englisch. Außerdem setzen sie die Klassennamen und Attributnamen direkt ein. Die Namen der Klassen und Attribute wählen wir normalerweise nach den Bedürfnissen der Programmierer, für die Benutzer sind gelegentlich andere Vokabeln angebrachter.

Der Aufbau einer Fehlermeldung bei einer fehlgeschlagenen Validierung ist im Bild zu sehen:

Zusammenbau der Übersetzung von Fehlermeldungen der Model-Validierungen in Rails

Zur Anzeige auf Deutsch müssen wir die Texte nur mit den richtigen Schlüsseln in unsere yaml-Übersetzungdatei eintragen. Die Rumpfsätze sind im Bereich (scope) activerecord.messages definiert. Da diese Sätze für alle Anwendungen gleich sind, gibt es schon für viele Sprachen und auch für Deutsch schon Standardübersetzungen.

Übersetzung des Models

Die Namen der Felder und Objekte müssen wir in der yaml-Datei im Bereich activerecord.models.classname beziehungsweise activerecord.attributs.classname.attributname definieren. Dann erkennt sie Rails und setzt sie korrekt in die automatischen Validierungsmeldungen ein.
Die zentrale Übersetzung der Klassennamen und Feldernamen ist auch für die Views interessant. So brauchen wir die Labels von Eingabefeldern nicht ein zweites oder drittes mal übersetzen.

activerecord:
  models:
    term: "Begriff"
  attributes:
    term:
      name: "Name"
      topic: "Sachgebiet"

Das macht für die Views nicht immer Sinn. Die Benutzerschnittstelle sollte sich nicht zu eng an das Model halten. Wichtig ist ja schließlich der Benutzer.

Validierungen mit eigenen Meldungen

Ein typischer Anwendungsfall für eine eigene Meldung ist die Überprüfung einer Telefonnummer. Die Methoden zur Spezifikation der Validierungen haben einen Parameter „message“. Damit sollte eine Übersetzung ja kein Problem sein: Wir sollten einfach hier die t()-Methode aufrufen können. Leider klappt es so nicht:

# funktioniert so nicht! Does not work!
validates_format_of( :fax,{
    :with => /\A[0-9 \+\-\/\(\)]+\Z/i,
    :message => t('activerecord.errors.messages.bad_phone'),
    :allow_blank => true})

Je nach Version und des verwendeten Deployments startet Rails gar nicht erst hoch oder liefert die gewünschte Übersetzung nicht. Was läuft schief?

Die Sprache ist ja immer pro Request gesetzt. Bei der Definition der deklarativen Valdierungen im Model gibt es noch gar keinen Request! Es macht deshalb keinen Sinn zu diesen Zeitpunkt die translate-Methode aufzurufen.

Aber wie lösen wir das Problem? Typisch für Rails ist die Lösung schon eingebaut. Wenn man sie nicht kennt, kann man sich allerdings lange im Kreis drehen.
Wir können dem message – Parameter nämlich auch ein Symbol statt eines Strings mitgeben: das Symbol wird erst ausgewertet, wenn die Fehlermeldung angezeigt werden soll. Das Symbol wird nacheinander in diesen speziellen Kontexten (scope) ausgewertet:

  • activerecord.errors.models.[model_name].attributes.[attribute_name]
  • activerecord.errors.models.[model_name]
  • activerecord.errors.messages

Unsere Validierung können wir demnach einfach so schreiben:

validates_format_of( :fax, {
     :with => /\A[0-9\+\-\/\(\)]+\Z/i,
     :message => :bad_phone,
     :allow_blank => true } )

Quellen:

Beschreibung (Guide) der Internationalization API:
guides.rubyonrails.org/i18n.html

Sprachdateien für Standardübersetzungen:
github.com/svenfuchs/rails-i18n/tree/master/rails/locale

Zu Teil 3 dieses Artikels

Beteilige dich an der Unterhaltung

1 Kommentar

  1. Hi,

    vielen Dank für den Tip mit den eigenen Fehlermeldungen bei der Validierung (kannte ich noch nicht).

    Du solltest vielleicht noch erwähnen, dass man die Übersetzungen in separaten Dateien verteilen kann.

    Und das die Verwendung der „activerecord.models.xxx“ so XXX.human_name (wobei XXX das Model darstellen) und „activerecord.attributes.xxx.yyy“ so XXX.human_attribute_name „yyy“ verwendet werden kann.

    Viel Spass
    Martin

Schreibe einen Kommentar