gettext - Programme internationalisieren


Sie sind hier: Rubygettext


Mittels dem Tool gettext können Programme für den internationalen Gebrauch vorbereitet werden.

Das Tool gibt es bei rubyforge und kann als gem installiert werden:

gem -install gettext

Eine Anleitung findet sich bei http://www.yotabanana.com/hiki/ruby-gettext-howto.html ich habe hier eine kurze Zusammenfassung meines Test erstellt.

Zur Klärung verschiedener Begriffe dieser Anleitung:

Grundsätzliche Struktur

  1. Alle sprachabhängigen Textausgaben (puts, Variablenzuweisung...) werden im Skript mit der Methode _() definiert.
    Beispiel: puts _("Hello World")
  2. Das Programm rgettext extrahiert die Texte und generiert eine pot-Datei
  3. Die pot-Datei wird als Vorlage für die Übersetzungen verwendet (po-Dateien)
  4. Das Programm rmsgfmt generiert aus den po-Dateien mo-Dateien
  5. Wird eine mo-Datei für das aktuelle Locale gefunden, werden diese Texte verwendet.
  6. Der Speicherort der mo-Datei muss dem System bekannt sein. Details siehe Suchpfad für Übersetzungen (mo-Datei)

Anmerkungen:

Erster Testlauf

Schritt eins: Programm anlegen

require 'gettext'
class Test
  include GetText

  bindtextdomain("test")
  bindtextdomain("test", :path => '.') 
  #~ bindtextdomain('test', :path => "#{File.dirname(__FILE__)}")

  def say_hello()
    puts _("Hello World")
  end

end

Alle Ausgaben müssen für die Benutzung mit gettext vorbereitet werden. Dazu werden alle Textausgaben (puts, Variablenzuweisung...) mit der Methode _ umgeben (im Beispiel in Methode say_hello).

bindtextdomain sorgt dabei dafür, das die Texte vorhanden sind.

Schritt 2: Texte extrahieren und übersetzen

Annahme: Obiges Programm hat den Namen test_gettext.

rgettext test_gettext.rb -o test.pot

Das Programm rgettext erzeugt eine pot-Datei (hier test.pot).

Um eine deutschsprachige Unterstützung des Programms zu erstellen kopiert man diese pot-Datei als test.po in einen Ordner de.

Deutsche Übersetzung erstellen

Die gerade erstellte de/test.po-Datei in einem Editor öffnen und bei

#: test_gettext.rb:20
msgid "Hello World"
msgstr ""

in die Zeile mit msgstr die Übersetzung eintragen:

#: test_gettext.rb:20
msgid "Hello World"
msgstr "Hallo Welt"

Der Text Hello World als Parameter der Funktion _() wird bei einem deutschen LOCALE in Hallo Welt übersetzt.

Übersetzung generieren.

Die Übersetzung in der po-Datei muss in eine mo-Datei übersetzt werden. Dazu dient das Programm rmsgfmt.

rmsgfmt de/test.po -o de/test.mo

Test

Danach kann das Programm erneut getestet werden. Auf einem deutschen Systemn sollte jetzt ein deutscher Text erscheinen.

Hinweise

Poedit - Ein Editor zum Übersetzen

Das Tool poedit erlaubt eine einfache Bearbeitung von po-Dateien.

Insbesonders können neue po-Dateien aus pot-Dateien angelegt werden und vor allem schon vorhandene po-Dateien können aus pot-Dateien aktualisiert werden. Dabei bleiben Übersetzungen erhalten, neue Texte werden eingefügt und die Referenzen zuu den Source-Code-Zeilen werden aktualisiert.

Suchpfad für Übersetzungen (mo-Datei)

Die m5o-Datei wird in vordefinierten Systempfaden gesucht.

Unter Windows und dem One-Click Installer werden die Sprachdateien in einem der folgenden Verzeichnisse erwartet:

C:/Program Files/ruby/share/locale/DE/LC_MESSAGES/
C:/Program Files/ruby/share/locale/de/LC_MESSAGES/
C:/Program Files/ruby/local/share/locale/DE/LC_MESSAGES/
C:/Program Files/ruby/local/share/locale/de/LC_MESSAGES/
C:/Program Files/ruby/lib/ruby/gems/1.8/gems/gettext-1.90.0/data/locale/DE/LC_MESSAGES/
C:/Program Files/ruby/lib/ruby/gems/1.8/gems/gettext-1.90.0/data/locale/de/LC_MESSAGES/
C:/Program Files/ruby/lib/ruby/gems/1.8/gems/gettext-1.90.0/data/locale/DE/
C:/Program Files/ruby/lib/ruby/gems/1.8/gems/gettext-1.90.0/data/locale/de/
C:/Program Files/ruby/lib/ruby/gems/1.8/gems/gettext-1.90.0/locale/DE/
C:/Program Files/ruby/lib/ruby/gems/1.8/gems/gettext-1.90.0/locale/de/

Ich schlage davon abweichend folgende Struktur vor:

test_gettext
test.pot
de/test.po
de/test.mo
en/test.po
en/test.mo

oder

test_gettext
test.pot
locale/de/test.po
locale/de/test.mo
locale/en/test.po
locale/en/test.mo

Soll so ein eigener Speicherort (z.B. beim Programm) verwendet werden, kann bindtextdomain den Pfad im Parameter :path definiert werden.

  bindtextdomain("test", :path => '.') 

Noch zu testen: Wie verhält sich der Suchpfad wenn das Hauptprogramm in anderen Ordnern liegt. Ist :path relativ zum Hauptprogramm oder zur Source-Datei?

Evtl. besser:

  bindtextdomain('test', :path => "#{File.dirname(__FILE__)}")

Texte mit Parametern

Zum Übersetzen mit Parametern gibt es prinzipiell zwei Möglichkeiten:

Stellungsparameter mit %

  def say_summe( p1, p2 )
    puts _("%1i and %1i give %i") % [ p1, p2, p1+p2 ]
    #de: %1i und %1i ergeben %i
  end

Vorteil:

Parameter als Hash

  def say_summe( p1, p2 )
    puts _("%{zahl1} and %{zahl2} give %{summe}") % {
      :zahl1 => p1, 
      :zahl2 => p2, 
      :summe => p1+p2 
      }
    #de: %{zahl1} und %{zahl2} ergeben immer noch %{summe}
  end

Vorteil:

Nachteil:

Sprachwechsel

Wird die Sprache im laufenden Programm gewechselt, muss das Binding erneuert werden.

Soll eine bestimmte Sprache eingestellt werden sollte es zum Programmanfang stattfinden.

class Test
  include GetText
  bindtextdomain("test")

  def say_hello
    puts _("Hello World")
  end

end

Test.new.say_hello  # -> Hallo Welt

#Sprache umsetzen
Locale.current = 'fr_fr'

#Nach dem Umsetzen des locales muss das binding erneuert werden.
class Test
    bindtextdomain("test", :path => '!!locale') 
end
  
Test.new.say_hello  #-> Bonjour tout le monde

Tipp

Die Klassen (oder Objecte...) sollten die Möglichkeit eines Sprachwechsel enthalten.

class Test
  include GetText
  def self#set_language()
    bindtextdomain("test", :locale => language.to_str ) 
  end
  set_language( Locale.current )  #initialisieren
  
  def say_hello
    puts _("Hello World")
  end

end
#...
Test.set_language('fr_fr')
#...

Evtl. macht es Sinn, ein Ereignis Sprachwechsel zu definieren und zu abonnieren.