Phoenix I18n

In meinen vorherigen Artikeln habe ich die verschiedenen Aspekte von Elixir - einer modernen funktionalen Programmiersprache - behandelt. Heute möchte ich jedoch von der Sprache abrücken und über ein sehr schnelles und zuverlässiges MVC-Framework namens Phoenix sprechen, das in Elixir geschrieben ist.

Dieser Rahmen ist vor fast fünf Jahren aufgetaucht und hat seitdem eine gewisse Zugkraft erhalten. Natürlich ist es noch nicht so beliebt wie Rails oder Django, aber es hat ein großes Potenzial und ich mag es wirklich.

In diesem Artikel erfahren Sie, wie Sie I18n in Phoenix-Anwendungen einführen. Was ist I18n, du fragst? Nun, es ist ein Numeronym, das "Internationalisierung" bedeutet, da zwischen dem ersten Buchstaben "i" und dem letzten "n" genau 18 Zeichen stehen. Wahrscheinlich haben Sie auch eine getroffen L10n Numeronym, was "Lokalisierung" bedeutet. Entwickler sind heutzutage so faul, dass sie nicht mal ein paar zusätzliche Charaktere schreiben können?

Internationalisierung ist ein sehr wichtiger Prozess, insbesondere wenn Sie die Anwendung von Menschen aus aller Welt vorhersehen. Schließlich kennt nicht jeder Englisch gut, und die Übersetzung der App in die Muttersprache eines Benutzers macht einen guten Eindruck.

Es scheint, dass sich der Prozess der Übersetzung von Phoenix-Anwendungen etwas von der Übersetzung von Rails-Apps unterscheidet (sie ähnelt jedoch dem gleichen Prozess in Django). Für die Übersetzung von Phoenix-Anwendungen verwenden wir eine beliebte Lösung namens Gettext, die bereits seit über 25 Jahren existiert. Gettext arbeitet mit speziellen Dateitypen, nämlich PO und POT, und unterstützt Funktionen wie Scoping, Pluralisierung und andere Goodies. 

In diesem Beitrag werde ich Ihnen erklären, was Gettext ist, wie sich PO von POT unterscheidet, wie Nachrichten in Phoenix lokalisiert werden und wo Übersetzungen gespeichert werden. Außerdem erfahren Sie, wie Sie das Gebietsschema der Anwendung ändern und mit Pluralisierungsregeln und Domänen arbeiten.

Sollen wir anfangen?

Internationalisierung mit Gettext

Gettext ist ein von Sun Microsystems 1990 erstmals eingeführtes Open Source-Internationalisierungstool. 1995 brachte GNU eine eigene Version von Gettext heraus, die heute als beliebteste Version gilt (die neueste Version war 0.19.8 Zeitpunkt des Schreibens dieses Artikels). Mit Gettext können mehrsprachige Systeme jeder Größe und Art erstellt werden, von Web-Apps bis zu Betriebssystemen. Diese Lösung ist ziemlich komplex, und wir werden natürlich nicht alle Funktionen besprechen. Die vollständige Gettext-Dokumentation finden Sie unter gnu.org.

Gettext bietet Ihnen alle notwendigen Werkzeuge zur Lokalisierung und gibt einige Anforderungen an, wie die Übersetzungsdateien benannt und organisiert werden sollen. Zum Hosten von Übersetzungen werden zwei Dateitypen verwendet: PO und MO.

PO (Tragbares Objekt) Dateien speichern Übersetzungen für bestimmte Zeichenfolgen sowie Pluralisierungsregeln und Metadaten. Diese Dateien haben eine recht einfache Struktur und können leicht von einem Menschen bearbeitet werden. In diesem Artikel werden wir uns an sie halten. Jede PO-Datei enthält Übersetzungen (oder einen Teil der Übersetzungen) für eine einzige Sprache und sollte in einem Verzeichnis gespeichert werden, das nach dieser Sprache benannt ist: en, fr, de, usw.

MO (Maschinenobjekt) Dateien enthalten binäre Daten, die nicht direkt von einem Menschen bearbeitet werden sollen. Es ist schwieriger, mit ihnen zu arbeiten, und die Diskussion über sie ist nicht Gegenstand dieses Artikels.

Um es komplexer zu machen, gibt es auch POT (Portable Object Template) Dateien. Sie enthalten nur zu übersetzende Datenstrings, nicht jedoch die Übersetzungen selbst. Grundsätzlich werden POT-Dateien nur als Blaupause verwendet, um PO-Dateien für verschiedene Ländereinstellungen zu erstellen.

Beispiel einer Phoenix-Anwendung

Okay, jetzt lass uns weiter üben! Wenn Sie mitverfolgen möchten, stellen Sie sicher, dass Sie Folgendes installiert haben:

  • OTP (Version 18 oder höher)
  • Elixier (1.4+)
  • Phoenix Framework (Ich werde Version 1.3 verwenden)

Erstellen Sie eine neue Beispielanwendung ohne Datenbank, indem Sie Folgendes ausführen:

mix phx.new i18ndemo --no-ecto

--kein ecto besagt, dass die Datenbank nicht von der App verwendet werden sollte (Ecto ist ein Werkzeug zur Kommunikation mit der DB selbst). Beachten Sie, dass der Generator möglicherweise einige Minuten benötigt, um alles vorzubereiten.

Jetzt benutzen CD zu dem neu erstellten gehen i18ndemo Ordner und führen Sie den folgenden Befehl aus, um den Server zu starten:

mische phx.server

Öffnen Sie als Nächstes den Browser und navigieren Sie zu http: // localhost: 4000, wo Sie ein "Willkommen in Phoenix" sehen sollten Botschaft.

Hallo, Gettext!

Das Interessante an unserer Phoenix-App und insbesondere die Begrüßung ist, dass Gettext bereits standardmäßig verwendet wird. Mach weiter und öffne die demo / lib / demo_web / templates / page / index.html.eex Datei, die als Standardstartseite dient. Entfernen Sie alles außer diesem Code:

<%= gettext "Welcome to %name!", name: "Phoenix" %>

Diese einladende Nachricht verwendet a gettext Funktion, die eine zu übersetzende Zeichenfolge als erstes Argument akzeptiert. Diese Zeichenfolge kann als a betrachtet werden Übersetzungsschlüssel, Es unterscheidet sich jedoch etwas von den Schlüsseln, die in Rails I18n und einigen anderen Frameworks verwendet werden. In Rails hätten wir einen ähnlichen Schlüssel verwendet Seite.Willkommen, wohingegen der übersetzte String hier ein Schlüssel ist selbst. Wenn die Übersetzung nicht gefunden werden kann, können wir diese Zeichenfolge direkt anzeigen. Sogar ein Benutzer, der Englisch schlecht beherrscht, kann zumindest ein grundlegendes Gefühl dafür bekommen, was vor sich geht.

Dieser Ansatz ist ziemlich praktisch, halten Sie für einen Moment an und denken Sie darüber nach. Sie haben eine Anwendung, bei der alle Nachrichten auf Englisch sind. Wenn Sie es internationalisieren möchten, müssen Sie im einfachsten Fall Ihre Nachrichten mit dem gettext Funktion und Bereitstellung von Übersetzungen für sie (später werden wir feststellen, dass das Extrahieren der Schlüssel leicht automatisiert werden kann, was die Sache noch schneller macht).

Okay, kehren wir zu unserem kleinen Code-Snippet zurück und schauen uns das zweite Argument an gettext: Name: "Phoenix". Dies ist ein sogenannter Bindung-ein Parameter, der mit % dass wir in die Übersetzung interpolieren möchten. In diesem Beispiel wird nur ein Parameter aufgerufen Name.

Wir können dieser Seite zu Demonstrationszwecken noch eine weitere Nachricht hinzufügen: 

<%= gettext "Welcome to %name!", name: "Phoenix" %>

<%= gettext "We are using version %version", version: "1.3" %>

Neue Übersetzung hinzufügen

Nun, da wir zwei Nachrichten auf der Stammseite haben, wo sollten wir Übersetzungen für sie hinzufügen? Es scheint, dass alle Übersetzungen unter gespeichert sind priv / gettext Ordner, der eine vordefinierte Struktur hat. Nehmen wir uns einen Moment Zeit, um zu besprechen, wie Gettext-Dateien organisiert werden sollen (dies gilt nicht nur für Phoenix, sondern auch für jede App, die Gettext verwendet)..

Zunächst sollten wir einen Ordner erstellen, der nach dem Gebietsschema benannt ist, für das Übersetzungen gespeichert werden sollen. Darin sollte sich ein Ordner befinden LC_MESSAGES ein oder mehrere enthalten .po Dateien mit den tatsächlichen Übersetzungen. Im einfachsten Fall hätten Sie eine default.po Datei pro Gebietsschema. Standard Hier ist der Name der Domäne (oder des Bereichs). Domänen werden verwendet, um Übersetzungen in verschiedene Gruppen zu unterteilen. Beispielsweise können Domänen benannt sein Administrator, Wysiwig, Wagen, und andere. Dies ist praktisch, wenn Sie eine große Anwendung mit Hunderten von Nachrichten haben. Für kleinere Apps jedoch eine Sohle Standard Domain ist genug. 

So könnte unsere Dateistruktur so aussehen:

  • en
    • LC_MESSAGES
      • default.po
      • admin.po
  • ru
    • LC_MESSAGES
      • default.po
      • admin.po

Zum Erstellen der PO-Dateien benötigen wir zunächst die entsprechende Vorlage (POT). Wir können es manuell erstellen, aber ich bin zu faul, es so zu machen. Lassen Sie uns stattdessen den folgenden Befehl ausführen:

mische gettext.extract

Es ist ein sehr praktisches Tool, das die Projektdateien durchsucht und prüft, ob Gettext überall verwendet wird. Nachdem das Skript seine Arbeit beendet hat, erscheint ein neues priv / gettext / default.pot Eine Datei mit zu übersetzenden Zeichenfolgen wird erstellt.

Wie wir bereits wissen, sind POT-Dateien Vorlagen. Sie speichern also nur die Schlüssel selbst, nicht die Übersetzungen. Ändern Sie diese Dateien also nicht manuell. Öffnen Sie eine neu erstellte Datei und sehen Sie sich den Inhalt an:

## Diese Datei ist eine PO-Vorlagendatei. ## ## 'msgid's werden hier oft aus dem Quellcode extrahiert. ## Neue Übersetzungen nur manuell hinzufügen, wenn es sich um dynamische ## Übersetzungen handelt, die nicht statisch extrahiert werden können. ## ## Führen Sie 'mix gettext.extract' aus, um diese Datei auf ## date zu bringen. Lassen Sie 'msgstr' leer, um sie hier als keinen ##-Effekt zu ändern: Bearbeiten Sie sie stattdessen in PO-Dateien ('.po'). msgid "" msgstr "" #: lib / demo_web / templates / page / index.html.eex: 3 msgid "Wir verwenden Version% version" msgstr "" #: lib / demo_web / templates / page / index.html msgstr "Willkommen bei% name!" msgstr ""

Bequem, nicht wahr? Alle unsere Nachrichten wurden automatisch eingefügt, und wir können leicht erkennen, wo sie sich befinden. msgstr, wie Sie wahrscheinlich erraten haben, ist der Schlüssel, während msgstr wird eine Übersetzung enthalten.

Der nächste Schritt ist natürlich das Generieren einer PO-Datei. Lauf:

mix gettext.merge priv / gettext

Dieses Skript wird das verwenden default.pot Vorlage und erstellen Sie eine default.po Datei in der priv / gettext / de / LC_MESSAGES Mappe. Im Moment haben wir nur ein englisches Gebietsschema, die Unterstützung für eine andere Sprache wird jedoch auch im nächsten Abschnitt hinzugefügt.

Übrigens ist es möglich, die POT-Vorlage und alle PO-Dateien auf einmal zu erstellen oder zu aktualisieren, indem Sie den folgenden Befehl verwenden:

mix gettext.extract --merge

Jetzt öffnen wir die priv / gettext / de / LC_MESSAGES / default.po Datei, die folgenden Inhalt hat:

Die msgid -Dateien in dieser Datei stammen aus POT-Dateien (.pot). ## ## Fügen Sie 'msgid' hier nicht manuell hinzu, ändern oder entfernen Sie sie, da ## an die in der entsprechenden POT-Datei ## gebundenen (mit derselben Domäne) gebunden sind. ## ## Verwenden Sie 'mix gettext.extract --merge' oder 'mix gettext.merge' ##, um POT-Dateien in PO-Dateien zusammenzuführen. msgid "" msgstr "" "Sprache: de \ n" #: lib / demo_web / templates / page / index.html.eex: 3 msgstr "Wir verwenden Version% version" msgstr "" #: lib / demo_web / msgstr "Willkommen bei% name!" msgstr ""

In dieser Datei sollten wir die eigentliche Übersetzung durchführen. Natürlich macht es wenig Sinn, dies zu tun, da die Nachrichten bereits in Englisch verfasst sind. Fahren wir mit dem nächsten Abschnitt fort und fügen Sie Unterstützung für eine zweite Sprache hinzu.

Mehrere Locales

Natürlich ist das Standardgebietsschema für Phoenix-Anwendungen Englisch, aber diese Einstellung kann leicht geändert werden, indem Sie die Einstellungen ändern config / config.exs Datei. Setzen wir beispielsweise das Standardgebietsschema auf Russisch (Sie können sich gerne an eine andere Sprache Ihrer Wahl halten):

config: demo, I18ndemoWeb.Gettext, default_locale: "ru"

Es ist auch eine gute Idee, die vollständige Liste aller unterstützten Gebietsschemas anzugeben:

config: demo, I18ndemoWeb.Gettext, default_locale: "ru", Gebietsschemas: ~ w (en ru)

Jetzt müssen wir eine neue PO-Datei erstellen, die Übersetzungen für das russische Gebietsschema enthält. Dies kann durch Ausführen des ausgeführt werden gettext.merge Skript wieder, aber mit einem --Gebietsschema Schalter:

mix gettext.merge priv / gettext --locale ru

Offensichtlich a priv / gettext / ru / LC_MESSAGES Ordner mit der .po Dateien darin werden generiert. Beachten Sie übrigens, dass abgesehen von der default.po Datei haben wir auch errors.po. Dies ist ein Standardbereich für das Übersetzen von Fehlermeldungen. In diesem Artikel werden wir ihn jedoch ignorieren.

Jetzt optimieren Sie die priv / gettext / ru / LC_MESSAGES / default.po indem Sie einige Übersetzungen hinzufügen:

msgstr "Wir verwenden Version% version" msgstr "Используется версия% version" #: lib / demo_web / templates / page.html msgstr "Willkommen bei% name!" msgstr "Добро пожаловать в приложение% name!"

Abhängig vom gewählten Gebietsschema wird Phoenix nun entweder eine englische oder eine russische Übersetzung ausführen. Aber halt! Wie können wir in unserer Anwendung tatsächlich zwischen Gebietsschemas wechseln? Fahren wir mit dem nächsten Abschnitt fort und finden Sie es heraus!

Zwischen Ländereinstellungen wechseln

Jetzt, da einige Übersetzungen vorhanden sind, müssen wir unseren Benutzern ermöglichen, zwischen Ländereinstellungen zu wechseln. Es scheint, dass es einen Plug-In eines Drittanbieters gibt, der set_locale heißt. Es funktioniert, indem das ausgewählte Gebietsschema aus der URL oder extrahiert wird Accept-Language HTTP-Header Um ein Gebietsschema in der URL anzugeben, geben Sie Folgendes ein http: // localhost: 4000 / de / some_path. Wenn das Gebietsschema nicht angegeben ist (oder wenn eine nicht unterstützte Sprache angefordert wurde), geschieht eine der beiden folgenden Situationen:

  • Wenn die Anfrage ein Accept-Language Der HTTP-Header und dieses Gebietsschema werden unterstützt. Der Benutzer wird auf eine Seite mit dem entsprechenden Gebietsschema umgeleitet.
  • Andernfalls wird der Benutzer automatisch zu einer URL umgeleitet, die den Code des Standardgebietsschemas enthält.

Öffne das  mix.exs ablegen und ablegen set_locale zum deps Funktion:

 defp deps endet [#… : set_locale, "~> 0.2.1"]

Wir müssen es auch dem hinzufügen Anwendung Funktion:

 def application do [mod: Demo.Application, [], extra_applications: [: logger,: runtime_tools,: set_locale]] enden

Als nächstes installieren Sie alles:

mix deps.get

Unser Router befindet sich am lib / demo_web / router.ex erfordert auch einige Änderungen. Insbesondere müssen wir einen neuen Plug hinzufügen :Browser Pipeline:

 pipeline: browser do #… set SetLocale, gettext: DemoWeb.Gettext, default_locale: "ru" Ende

Erstellen Sie außerdem einen neuen Bereich:

 Bereich "/: locale", DemoWeb do pipe_through: browser get "/", PageController,: Indexende

Und das ist es! Sie können den Server starten und zu navigieren http: // localhost: 4000 / ru und http: // localhost: 4000 / de. Beachten Sie, dass die Nachrichten korrekt übersetzt werden. Genau das brauchen wir!

Alternativ können Sie ein ähnliches Feature auch selbst programmieren, indem Sie einen Modulstecker verwenden. Ein kleines Beispiel finden Sie im offiziellen Phoenix Guide.

Als letztes zu erwähnen ist, dass Sie in bestimmten Fällen ein bestimmtes Gebietsschema erzwingen müssen. Verwenden Sie dazu einfach eine with_locale Funktion:

Gettext.with_locale I18ndemoWeb.Gettext, "de", fn -> MyApp.I18ndemoWeb.gettext ("test") endet

Pluralisierung

Wir haben die Grundlagen für die Verwendung von Gettext mit Phoenix gelernt. Daher ist es an der Zeit, etwas komplexere Dinge zu diskutieren. Pluralisierung Ist einer von ihnen. Grundsätzlich ist das Arbeiten mit Plural- und Singularformen eine sehr häufige, aber möglicherweise komplexe Aufgabe. Auf Englisch sind die Dinge mehr oder weniger offensichtlich, da Sie "1 Apfel", "2 Äpfel", "9000 Äpfel" usw. haben (obwohl "1 Ochse", "2 Ochsen"!).

In einigen anderen Sprachen wie Russisch oder Polnisch sind die Regeln leider komplexer. Im Fall von Äpfeln würden Sie beispielsweise "1 яблоко", "2 яблока", "9000 яблок" sagen. Aber zum Glück hat Phoenix eine Gettext.Plural Verhalten (Sie können das Verhalten in Aktion in einem meiner vorherigen Artikel sehen), das viele verschiedene Sprachen unterstützt. Deshalb müssen wir nur das nutzen ngettext Funktion.

Diese Funktion akzeptiert drei erforderliche Argumente: eine Zeichenfolge in Singularform, eine Zeichenfolge in Pluralform und Anzahl. Das vierte Argument ist optional und kann Bindungen enthalten, die in die Übersetzung interpoliert werden sollen.

Mal schauen ngettext in Aktion, indem Sie sagen, wie viel Geld der Benutzer hat, indem Sie das ändern demo / lib / demo_web / templates / page / index.html.eex Datei:

<%= ngettext "You have one buck. Ow :(", "You have %count bucks", 540 %>

%Anzahl ist eine Interpolation, die durch eine Zahl ersetzt wird (540 in diesem Fall). Vergessen Sie nicht, die Vorlage und alle PO-Dateien nach dem Hinzufügen der obigen Zeichenfolge zu aktualisieren:

mix gettext.extract --merge

Sie werden sehen, dass zu beiden ein neuer Block hinzugefügt wurde default.po Dateien:

msgstr "Sie haben einen Dollar. Ow :(" msgid_plural "Sie haben% count bucks" msgstr [0] "" msgstr [1] ""

Wir haben hier nicht nur einen, sondern zwei Schlüssel gleichzeitig: in Singular- und Pluralform. msgstr [0] wird Text enthalten, der angezeigt werden soll, wenn nur eine Nachricht vorhanden ist. msgstr [1], enthält natürlich den Text, der angezeigt wird, wenn mehrere Nachrichten vorhanden sind. Dies ist okay für Englisch, aber nicht genug für Russisch, wo wir einen dritten Fall einführen müssen: 

msgid "Sie haben einen Dollar. Ow :(" msgid_plural "Sie haben% count bucks" msgstr [0] "У 1 доллар. Маловато будет!" msgstr [1] " ] "У вас% count долларов"

Fall 0 wird für 1 Bock und Fall verwendet 1 für null oder wenige Dollar. Fall 2 wird sonst verwendet.

Umfangreiche Übersetzungen mit Domains

Ein anderes Thema, das ich in diesem Artikel diskutieren wollte, ist gewidmet Domänen. Wie wir bereits wissen, werden Domänen für Übersetzungen verwendet, hauptsächlich in großen Anwendungen. Grundsätzlich wirken sie wie Namespaces.

Schließlich kann es vorkommen, dass derselbe Schlüssel an mehreren Stellen verwendet wird, er sollte jedoch etwas anders übersetzt werden. Oder wenn Sie viel zu viele Übersetzungen in einer einzigen haben default.po Datei und möchte sie irgendwie spalten. Dann können Domains sehr nützlich sein. 

Gettext unterstützt mehrere Domains aus der Box. Sie müssen nur das nutzen dgettext Funktion, die fast genauso funktioniert wie gettext. Der einzige Unterschied besteht darin, dass der Domänenname als erstes Argument akzeptiert wird. Lassen Sie uns zum Beispiel eine Benachrichtigungsdomäne einführen, in der Benachrichtigungen angezeigt werden. Fügen Sie dem Code drei weitere Codezeilen hinzu demo / lib / demo_web / templates / page / index.html.eex Datei:

<%= dgettext "notifications", "Heads up: %msg", msg: "something has happened!" %>

Jetzt müssen wir neue POT- und PO-Dateien erstellen:

mix gettext.extract --merge

Nachdem das Skript seine Arbeit beendet hat, notifications.pot sowie zwei Benachrichtigungen.po Dateien werden erstellt. Beachten Sie erneut, dass sie nach der Domäne benannt sind. Jetzt müssen Sie nur noch die Übersetzung für die russische Sprache hinzufügen, indem Sie die priv / ru / LC_MESSAGES / notifications.po Datei:

msgstr "Heads up:% msg" msgstr "Внимание:% msg"

Was ist, wenn Sie eine unter einer bestimmten Domäne gespeicherte Nachricht in mehrere Felder aufteilen möchten? Dies ist so einfach wie das Verwenden von a dngettext Funktion. Es funktioniert genauso ngettext akzeptiert aber auch den Namen einer Domain als erstes Argument:

dgettext "domain", "Singular string% msg", "Plural string% msg", 10, msg: "demo"

Fazit

In diesem Artikel haben wir gesehen, wie Sie mithilfe von Gettext Internationalisierung in eine Phoenix-Anwendung einführen können. Sie haben gelernt, was Gettext ist und mit welcher Art von Dateien es arbeitet. Wir haben diese Lösung in Aktion, haben mit PO- und POT-Dateien gearbeitet und verschiedene Gettext-Funktionen verwendet.

Außerdem haben wir eine Möglichkeit gesehen, Unterstützung für mehrere Gebietsschemas hinzuzufügen, und es wurde eine Möglichkeit zum einfachen Wechseln zwischen diesen hinzugefügt. Schließlich haben wir gesehen, wie man Pluralisierungsregeln anwendet und wie man Übersetzungen mit Hilfe von Domänen einschränkt.

Hoffentlich war dieser Artikel für Sie nützlich! Wenn Sie mehr über Gettext im Phoenix-Framework erfahren möchten, lesen Sie den offiziellen Leitfaden, der nützliche Beispiele und API-Referenz für alle verfügbaren Funktionen enthält.

Ich danke dir, dass du bei mir bleibst und bis bald!