Python ist das Herzstück weltberühmter Anwendungen wie Youtube, Instagram und Pinterest. Um auf dem Weltmarkt voranzukommen, muss eine Anwendung lokalisiert werden, dh an die Merkmale eines bestimmten Landes angepasst und internationalisiert werden - dh Inhalte übersetzen. In diesem Artikel werden wir unsere Erfahrungen zur Beschleunigung der Übersetzungsautomatisierung und zur Lösung einiger typischer Probleme in diesem Bereich teilen.

Einleitung
Dies ist eine kurze Anleitung zur Internationalisierung von (i18n) Python-Anwendungen. Diese Anleitung ist für alle Programmierer mit Erfahrung in der Python-Entwicklung interessant. Das Lesen eines Artikels dauert 10-15 Minuten.
Wir werden das bewährte gettext-Tool verwenden, das in der Python-Sprache enthalten ist.
Zunächst werden wir verstehen, was Internationalisierung ist:
Internationalisierung (I18N) ist der Prozess der Anpassung einer Anwendung an die Sprachen verschiedener Länder und Regionen, in denen sie nicht entwickelt wurde.
Es gibt aber auch ein umfassenderes Konzept:
Bei der Lokalisierung (L10N) wird eine internationalisierte Anwendung an eine bestimmte Region oder Sprache angepasst, indem Komponenten für ein bestimmtes Gebietsschema hinzugefügt und Text übersetzt werden.
Lokalisierung bedeutet Übersetzung:
- Datums- und Uhrzeitformat;
- Zahlenformat;
- Zeitzone
- ein Kalender
- Währungsdarstellungen;
- Steuern / Mehrwertsteuer;
- Temperatur und andere Maßnahmen;
- Postleitzahlen, Telefone;
- Adressformatierung;
- Code der Abrechnung.

Die Lokalisierung geht über die Übersetzung von Inhalten in eine andere Sprache hinaus. Es gibt kulturelle und funktionale Parameter, die ebenfalls Aufmerksamkeit erfordern. In Nordamerika lautet das Datumsformat beispielsweise MM / TT / JJJJ, in den meisten asiatischen Ländern jedoch TT / MM / JJJJ.
Ein bekanntes Beispiel für einen AnwendungsübersetzungsfehlerEin weiteres Beispiel betrifft die Anzeige von Namen in Anwendungen. In den USA ist es akzeptabel, jemanden beim Namen anzurufen, und noch besser, der Kundenname wird in der Kopfzeile angezeigt, sobald sich der Kunde anmeldet. In Japan ist das Gegenteil der Fall: Jemanden beim Namen zu nennen ist unhöflich oder sogar beleidigend. Die Lokalisierung sollte dies berücksichtigen und die Verwendung von Namen für ein japanisches Publikum vermeiden.
In diesem Artikel werden wir nur die Internationalisierung betrachten, aber Lokalisierungsmechanismen sind auf ähnliche Weise aufgebaut. Die in diesem Artikel erwähnten Bibliotheken unterstützen die Anwendungslokalisierung.
Haupttypen
Die Internationalisierung gliedert sich in:
- Übersetzung von Daten direkt in Python-Skripten.
- Übersetzung von Daten in Template-Engines.
- Übersetzung von in einer Datenbank gespeicherten Daten.
1. Übersetzung von Python-Skriptdaten
Damit unsere Internationalisierung funktioniert, müssen wir uns mit der Babel-Bibliothek und dem Distutils-Toolkit befassen, um die Zusammenstellung des Projekts für den Verkauf und darüber hinaus zu verwalten.
Übersetzungsvorbereitung
Zunächst müssen wir eine Liste mit Übersetzungen erstellen. Zunächst installieren wir die
Babel- Bibliothek - eine allgemein anerkannte Python-Bibliothek zum Lokalisieren und Konvertieren von Daten und Währungen mit praktischen Ergänzungen zum Erstellen des Projekts (siehe unten).
Python bietet ein Toolkit für Mehrsprachigkeit - gettext. GNU gettext ist eine universelle Lokalisierungslösung, die Unterstützung für andere Programmiersprachen in mehrsprachigen Nachrichten bietet. Gettext wird nicht nur in vielen Programmiersprachen, sondern auch bei der Übersetzung von Betriebssystemen verwendet, es ist eine erprobte, frei verbreitete Software, die auf
github verfügbar
ist .
Damit die Übersetzungen funktionieren, müssen Sie das gettext-Modul importieren und die Skripte mit den Übersetzungen an die Eingabe übergeben. Zunächst markieren wir alle übersetzten Zeichenketten mit der Sonderfunktion _ ('some_text'). Der Aufruf dieser Funktion im Projekt sieht folgendermaßen aus:
import gettext import os localedir = os.path.join(os.path.abspath('/path/to/locales'), 'locales') translate = gettext.translation('domain_name', localedir, ['ru']) _ = translate.gettext print(_('some_text')) print(_('some_text_2'))
Erstellen Sie in einem kleinen Codeteil ein Internationalisierungsobjekt, das das Verzeichnis 'locales' als Quelle für übersetzte Phrasen verwendet. Das 'locales'-Verzeichnis wurde noch nicht erstellt, aber es ist das Verzeichnis, in dem die Anwendung zur Laufzeit nach Übersetzungen sucht.
Der Kürze halber wird die Funktion translate.gettext im Folgenden als _ bezeichnet. Unterstrich ist der gebräuchliche Name für diese Funktion, der von der Python-Community erkannt wird.
Die Funktion _ () markiert die zu übersetzenden Zeilen. Das gettext-Modul wird vom xgettext-Tool begleitet, das die Zeichenfolgenmarkierungen _ () nach Code analysiert und eine Portable Object Template (Pot-Datei) erstellt. Um die Pot-Datei zu erstellen, kehren wir zu der installierten Babel-Bibliothek zurück, die viele Funktionen zur Unterstützung der Internationalisierung bietet. Babel erweitert das Build-Skript setup.py, das entweder mit der Standard-Python-Distutils-Bibliothek oder dem Setuptools-Paket eines Drittanbieters Ihrer Wahl geschrieben werden kann. Die Zusammenstellung von Python-Modulen würde den Rahmen unseres Artikels sprengen, nähere Informationen finden Sie in der
Dokumentation . Sie müssen lediglich eine setup.py-Datei mit folgendem Inhalt erstellen:
from babel.messages import frontend as babel from distutils.core import setup setup(name='foo', version='1.0', cmdclass = {'extract_messages': babel.extract_messages, 'init_catalog': babel.init_catalog, 'update_catalog': babel.update_catalog, 'compile_catalog': babel.compile_catalog,} )
So erstellten wir Anweisungen zum Aufbau des Projekts und fügten vier Internationalisierungsteams aus der Babel-Bibliothek hinzu. Betrachten Sie diese Befehle in der Reihenfolge ihrer Verwendung genauer.
extract_messagesDieser Befehl ist ein Wrapper über das GNU-Tool xgettext, mit dem _ () übersetzbare Tags in eine Pot-Datei geparst werden. Zum Ausführen benötigen Sie mehrere Einstellungen für die Assembly. Erstellen Sie dazu im Stammverzeichnis die Datei setup.cfg mit dem Inhalt:
[extract_messages] input_dirs = foobar output_file = foobar/locales/messages.pot
- eingabeverzeichnisse - Der Name des Verzeichnisses, aus dem alle Bezeichnungen im Code _ () für Übersetzungen ausgewählt werden.
- output_file - Pfad für die resultierende .pot-Datei
Führen Sie zum Ausführen des Befehls in der Konsole Folgendes aus:
$ python setup.py extract_messages
running extract_messages extracting messages from foobar/__init__.py extracting messages from foobar/core.py ... writing PO template file to foobar/locales/messages.pot
In der Topf-Datei werden markierte Zeilen in einer Liste gesammelt, aus der Übersetzer dann Übersetzungen für jede der gewünschten Sprachen erstellen können.
Als nächstes müssen Sie Übersetzungen für mehrere Sprachen erstellen. Verwenden Sie dazu die folgenden babel-Befehle.
init_catalogDieser Befehl ist ein Wrapper über das GNU-Tool msginit, das ein neues Übersetzungsverzeichnis basierend auf der Pot-Datei erstellt.
$ python setup.py init_catalog -l en -i foobar/locales/messages.pot \ -o foobar/locales/en/LC_MESSAGES/base.po
running init_catalog creating catalog 'foobar/locales/en/LC_MESSAGES/messages.po' based on 'foobar/locales/messages.pot'
Wichtig! Lokalisierungsdateien werden gemäß der Konvention auf eine bestimmte Weise gespeichert:
locales // LC_MESSAGES / .po
- ein Verzeichnis mit Übersetzungen in eine bestimmte Sprache, in unserem Fall Englisch (en). Es kann auch ein Verzeichnis mit Übersetzungen geben, die nicht nur in eine bestimmte Sprache übersetzt werden, sondern auch zusätzliche Funktionen berücksichtigen. Eine englische Übersetzung für die USA lautet beispielsweise "en_US".
- Domain mit Übersetzungen. Wenn unsere Anwendung wächst, werden Übersetzungen in Domänen unterteilt, um eine Datei nicht zu überlasten.
update_catalogDieser Befehl ist ein Wrapper für das GNU-Tool msgmerge, mit dem vorhandene Übersetzungsverzeichnisse für * .po-Dateien aktualisiert werden.
Wenn wir neue Übersetzungen hinzufügen, führen wir einfach den folgenden Befehl aus:
$ python setup.py update_catalog -l en -i foobar/locales/messages.pot \ -o foobar/locales/en/LC_MESSAGES/base.po
running update_catalog updating catalog 'foobar/locales/en/LC_MESSAGES/base.po' based on 'foobar/locales/messages.pot'
Wir können die Lokalisierung auch auf Russisch angeben, indem wir ru anstelle von en angeben.
compile_catalogDer letzte Befehl ist ein Wrapper über das GNU-Tool msgfmt. Es verwendet übersetzbare Nachrichten aus * .po-Dateien und kompiliert sie in binäre * .mo-Dateien, um die Leistung zu optimieren.
$ python setup.py compile_catalog --directory foobar/locales --domain base
running compile_catalog compiling catalog to foobar/locales/en/LC_MESSAGES/base.mo
--directory - Pfad zum Verzeichnis mit Lokalisierung,
--domain - ein Flag zum Angeben einer Übersetzungsdomäne, wir geben es in Übereinstimmung mit vorhandenen Anwendungsdomänen an.
Python-Skripte funktionieren nur mit optimierten * .mo-Übersetzungen. Daher ist es bei jeder Änderung erforderlich, die Dateien mit Lokalisierung neu zu kompilieren, damit sie in der Anwendung angezeigt werden. Um mit Übersetzungsdateien zu arbeiten, können Sie die poedit-Anwendung verwenden - sie ist für alle Betriebssysteme verfügbar und frei verteilt.
poedit - ÜbersetzungsanwendungJede Übersetzung wird als separate Zeile angezeigt, was praktisch ist. Nach Abschluss der Arbeit mit Übersetzungen wird beim Speichern von Änderungen automatisch eine * .mo-Binärdatei mit allen Änderungen kompiliert.
Folglich sieht die Struktur der Übersetzungskataloge folgendermaßen aus:
locales ├── en │ └── LC_MESSAGES │ ├── base.mo │ └── base.po ├── ru │ └── LC_MESSAGES │ ├── base.mo │ └── base.po └── messages.pot
Namenskonvention für Übersetzungsmarkerpo-Dateien enthalten Textübersetzungen und werden logisch zu einer Datei mit einem gemeinsamen Namen zusammengefasst. Diese Gruppen werden als Domänen bezeichnet. Im obigen Beispiel gibt es nur eine Domain mit dem Namen base. In großen Anwendungen wird es mehr Domänen geben, und Übersetzungslisten müssen unter Berücksichtigung der Struktur der Anwendung erstellt werden.
Es ist notwendig, die Namen der Übersetzungstoken einheitlich zu halten, um weitere Verwirrung in den Übersetzungen zu vermeiden. Beispielsweise haben wir ein Formular zum Speichern von Benutzerdaten auf der Benutzerprofilseite:
profile.user_form.component.title: Benutzerdaten
profile.user_form.component.save: Speichern
profile.user_form.field.username: Benutzername
profile.user_form.field.password: Passwort
AnwendungsbereitstellungUm die Anwendung im Docker bereitzustellen und bereitzustellen, müssen Sie die Übersetzungsdateien mit dem folgenden Befehl in * .mo-Binärdateien kompilieren:
$ python setup.py compile_catalog --domain <>
Wir empfehlen, * .mo und * .pot Dateien in .gitignore auszuschließen:
# Übersetzungen
* .mo
* .pot2. Übersetzung von Daten in Template-Engines
Mit der Lokalisierung im Templating ist alles ein bisschen einfacher. Betrachten Sie die beliebteste Python-Template-Engine - Jinja. Für diese Template-Engine ist bereits eine Unterstützung für die Gettext-Lokalisierung durch Add-Ons implementiert. Um das Add-On zu aktivieren, müssen Sie den Pfad zum Add-In-Modul im Umgebungskonstruktor angeben. Für mehrsprachige Plattformen müssen Sie die Übersetzungen einmal herunterladen und während der Anwendungsinitialisierung Übersetzungsobjekte zum Umgebungsobjekt hinzufügen:
translations = get_gettext_translations() env = Environment(extensions=['jinja2.ext.i18n']) env.install_gettext_translations(translations)
Dann verwenden wir in den Vorlagen nur die Konstrukte:
{{ gettext('some_text') }} {{ gettext('Hello %(name)s!')|format(name='World') }}
3. Übersetzung der in der Datenbank gespeicherten Daten
Lassen Sie uns Optionen für die Arbeit mit Übersetzungen in den gängigsten relationalen Datenbanken betrachten. Es ist zu beachten, dass die Implementierung von Übersetzungen und Lokalisierung für noSQL- und newSQL-Datenbanken ähnlich ist.
Hinweis: Der Fall wird nicht berücksichtigt, wenn die Übersetzung für jede Sprache in einer separaten Spalte gespeichert ist. Eine solche Implementierung beinhaltet Skalierungsbeschränkungen und andere Risiken mit weiterer Anwendungsunterstützung.
1) Separate Zeilen für jede Sprache
Bei diesem Ansatz basiert die Übersetzung in eine bestimmte Sprache in den Zeilen für jede Sprache auf dem Wert der Spalte, z. B. language_code. Wenn sich der Wert en in dieser Spalte befindet, sollten sich alle übersetzten Werte auf das angegebene Land und die angegebene Region beziehen.

Für das beschriebene Schema sollten die Daten in der Tabelle folgendermaßen aussehen:
Vorteile:- Einfache und effiziente Implementierung.
- Einfache Abfragen bei Verwendung eines bestimmten Sprachcodes.
Nachteil:- Mangel an Zentralisierung
Übersetzungen in verschiedene Sprachen können in verschiedenen Tabellen gespeichert werden. Daher wissen Sie nicht, in wie viele Sprachen Ihre Anwendung vollständig übersetzt ist.
Diese Lösung eignet sich für Anwendungen, bei denen zunächst nicht alle Daten vollständig internationalisiert werden müssen. Es ist jedoch möglich, Übersetzungen für neue Regionen hinzuzufügen, wenn das Geschäft wächst.
Die Datenanforderung lautet wie folgt:
SELECT p.product_name, p.price, p.description FROM product p WHERE p.language_code = @language_code;
2) Tabellen mit Übersetzungen trennen
In diesem Ansatz erstellen wir für jede zu lokalisierende Tabelle Tabellen mit Übersetzungen.
Vorteile:- Es ist nicht erforderlich, Tabellen für nicht übersetzte Daten zu verknüpfen.
- Abfragen werden einfach, da separate Tabellen für die Übersetzung vorhanden sind.
- Es gibt keine Diskrepanzen in den Daten.
- Zusätzlich zu Übersetzungen ist es möglich, den Rest der Daten in der Sprachtabelle effektiv zu lokalisieren.
Nachteil:- In großen Anwendungen ist die Übersetzungstabelle aufgebläht und verlangsamt sich. Bei der Optimierung der Anwendung muss die Datenmigration in separaten Tabellen implementiert werden.
Die Datenanforderung lautet wie folgt:
SELECT tp.text, p.price, tc.text, c.contact_name FROM order_line o, product p, customer c, translation tp, translation tc, language l WHERE o.product_id = p.id AND o.customer_id = c.id AND p.name_translation_id = tp.id AND c.name_translation_id = tc.id AND tp.language_id = l.id AND tc.language_id = l.id AND l.name = @language_code AND o.id = ***;
3) Entitäten für übersetzte und nicht übersetzte Felder erstellen
In dieser Lösung erweitern Entitätstabellen, die ein oder mehrere übersetzte Felder enthalten, Daten durch nicht übersetzte.
Vorteile:- Übersetzungstabellen müssen nicht mit Tabellen kombiniert werden, die Daten enthalten, für die keine Übersetzung erforderlich ist. Das Abtasten solcher Daten hat daher eine bessere Leistung.
- Es ist einfach, ORM-Abfragen zu schreiben,
- Eine einfache SQL-Abfrage, um übersetzten Text zu erhalten,
- Es ist einfach, die Übersetzung bestimmter Daten in alle verfügbaren Sprachen zu unterstützen.
Nachteil:- Die relative Komplexität der Implementierung.
Hier ist ein Beispiel für eine Abfrage, mit der übersetzter Text abgerufen wird:
SELECT pt.product_name, pt.description, p.price FROM order_line o, product p, product_translation pt, language l WHERE o.product_id = p.id AND AND p.id = pt.product_non_trans_id AND pt.language_id = l.id AND l.name = @language_code;
Schlussfolgerungen
Bei der Lokalisierung und Internationalisierung von Anwendungen für den internationalen Markt können verschiedene Methoden verwendet werden, die jeweils bestimmte Merkmale und Einschränkungen aufweisen.
In diesem Artikel haben wir folgende Arten der Internationalisierung untersucht:
- im Code: Wir verwenden Übersetzungen, wenn wir einen Dienst oder eine Anwendung mit GUI erstellen.
- in Templates: verwenden wir bei der Entwicklung einer Webanwendung ohne dynamisches Frontend;
- In der Datenbank: Wird zum Speichern von Benutzerdaten oder dynamisch generierten Daten verwendet.
Wir hoffen, dass unser Artikel Ihnen bei der Auswahl der für Ihr Projekt am besten geeigneten Methode hilft.