.. _internationalization: Internationalisierung ********************* Mehrsprachige Web-Anwendungen sind heute eher die Regel als die Ausnahme. Wir als Entwickler müssen zwei Arten von Daten unterscheiden: * statische Daten (Felder, Fehlermeldungen, Überschriften, etc) * dynamische Daten, Userdaten (Texte) .. admonition:: Internationalization vs Localization Internationalisierung und Lokalisierung ermöglichen uns, den Inhalt einer Webanwendung für verschiedene Gebietsschemas bereitzustellen. **Internationalisierung**, dargestellt durch i18n (18 ist die Anzahl der Buchstaben zwischen i und n), ist die Entwicklung der Anwendung, damit sie von verschiedenen Gebietsschemas verwendet werden kann. Dieser Prozess wird im Allgemeinen von Entwicklern durchgeführt. Die **Lokalisierung**, dargestellt durch l10n (10 ist die Anzahl der Buchstaben zwischen l und n), ist andererseits der Prozess der Übersetzung der Anwendung in eine bestimmte Sprache und ein bestimmtes ``Gebietsschema``. Dies wird in der Regel von Übersetzern erledigt. Die Anzeige des deutschen Datums ist ein Beispiel für die Lokalisierung. Django-Translation-Api ======================== Die Django-Übersetzungs-API bietet mehrere Dienstprogrammfunktionen, die bei der Übersetzung einer Anwendung unterstützen. Sie sind alle im Modul ``django.utils.translation`` verfügbar. In den meisten Fällen wird ``gettext()`` und ``gettext_lazy()`` verwendet. Installation von GNU gettext -------------------------------- Es muss GNU gettext installiert sein, um die nötigen Dateien zur Internationalisierung zu erzeugen. .. admonition:: GNU gettext GNU gettext ist eines der am weitesten verbreiteten Werkzeuge zur Internationalisierung von Software. Es bietet eine einfache und dennoch flexible Möglichkeit, die Software zu lokalisieren. ``LANGUAGE.po`` Dateien enthalten Strings in der Übersetzung in eine bestimmte Sprache. Es handelt sich um einfache Text-Dateien, die mit einem Editor bearbeitet werden können. Es ist sinnvoll, diese Dateien mit in die Versionskontrolle aufzunehmen, damit andere Entwickler kompilieren können. ``LANGUAGE.mo`` Dateien sind die binäre Repräsentation der po-Dateien und werden zur Laufzeit genutzt. Sie werden -anders als die po-Dateien- nicht versioniert. Django bietet durch die manage.py Kommandos ``makemessages`` und ``compilemessages`` die Erzeugung und kompilierung dieser Dateien. Installation unter Linux .......................... .. code-block:: bash sudo apt update sudo apt install gettext Installation unter Windows ........................... Für Windows lässt sich das Programm hier als .exe runterladen: ``_ Installation des gettext-Moduls für Python ------------------------------------------- .. code-block:: bash pip install python-gettext Settings.base.py anpassen --------------------------- Attribute für Lokalisierung und Internationalisierung setzen: .. code-block:: python from django.utils.translation import gettext_lazy as _ LANGUAGE_CODE = "de" USE_I18N = True USE_L10N = True USE_TZ = True ``LANGUAGE_CODE`` ist die Default-Sprache, ``TIME_ZONE`` ist die Sprache, in der Zeitangaben gerendert werden, ``USE_I18N`` ermöglicht den Umgang mit mehreren Sprachen, ``USE_L10N`` ermöglicht die Lokalisierung von Daten (Format Von Zeit und Zahlen), ``USE_TZ`` speichert ein Datetime immer in UTC, um Probleme mit anderen Zeitzonen und Sommerzeiten zu vermeiden. Einschränken der Sprachen ........................... Wir fügen die Sprachen, die wir für die Übersetzung zur Verfügung stellen möchten, in die Datei ``event_manager/event_manager/settings/base.py`` ein. Dies schränkt die Sprachen ein, in die ein Benutzer die Website übersetzen lassen kann. Mit dem Präfix ``_()`` stellen wir auch die Texte zur Sprachauswahl zum Übersetzen zur Verfügung.`` .. code-block:: python LANGUAGES = ( ('en', _('English')), ('de', _('German')), ) Standardmäßig wählt Django die Sprache aus, die in ``LANGUAGE_CODE`` in den ``settings.py`` hinterlegt ist. Damit aber der User selbst eine Sprache wählen kann, muss die ``LocaleMiddleware`` eingebunden werden. Middleware einbinden ------------------------- Um zu ermöglichen, dass der User seine Sprache selbst definieren kann, zb. per Dropdown-Menü, muss eine Middleware eingebunden werden. Die ``LocaleMiddleware`` sollte nach der ``Session-Middleware`` und -falls vorhanden- nach der ``CachingMiddleware`` eingebaut werden. Wir ändern nun unsere Datei ``event_manager/event_manager/settings/base.py`` wie folgt ab: .. code-block:: python MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.locale.LocaleMiddleware", # <= das hier einbinden "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ] Wie wählt LocaleMiddleware die Sprache? --------------------------------------- * Zunächst wird in der angeforderten URL nach dem Sprachpräfix gesucht. Dies wird nur durchgeführt, wenn die Funktion ``i18n_patterns`` in der Root-URLconf verwendet wird. * Andernfalls wird in der Session des aktuellen Benutzers nach dem Schlüssel ``LANGUAGE_SESSION_KEY`` gesucht. * Andernfalls wird nach einem Cookie gesucht. Der Name des verwendeten Cookies wird durch die Einstellung ``LANGUAGE_COOKIE_NAME`` festgelegt. (Der Standardname des Cookies ist ``django_language``). * Andernfalls wird der ``HTTP-Header Accept-Language`` überprüft. Dieser Header wird von Ihrem Browser gesendet und teilt dem Server mit, welche Sprache(n) Sie bevorzugen, nach Priorität geordnet. Django probiert jede Sprache im Header aus, bis es eine mit verfügbaren Übersetzungen findet. * Andernfalls wird die globale Einstellung ``LANGUAGE_CODE`` aus der ``settings/base.py`` verwendet. .. admonition:: Was ist der Unterschied zwischen gettext und gettext_lazy? gettext verweist auf den übersetzten String, gettext_lazy auf eine Referenz des Übersetzungsstring. Viele Teile im Django-Code werden nur einmal beim Start von Django ausgeführt. Dazu gehören zum Beispiel ``Models`` oder ``ModelForms``. Wenn wir im Model jetzt gettext verwenden würden, würde der User auch nach Sprachwechsel den schon übersetzen String sehen. Bei gettext_lazy ändert sich die Referenz und er sieht die richtige Übersetzung. In views hingegen kann man immer die gettext-Version nehmen. Mehr dazu in den Docs: '`_ Übersetzungen im Code ------------------------ Um die Möglichkeit der Übersetzung im Pythoncode zu bieten, müssen wir gettext aus den ``django.utils`` importieren. Für gewöhnlich gibt man ``gettext_lazy`` den Alias ``_``. Wir können einen Hilfetext für die Eingabefelder in der Admin (und Frontend) mit dem Attribut ``help_text`` in den models angeben: .. code-block:: python from django.utils.translation import gettext_lazy as _ ... Model Code description = models.TextField( null=True, blank=True, help_text=_("Die Beschreibung der Kategorie") ) man beachte das Attribut ``help_text=_("Die Beschreibung der Kategorie")`` Der Wert des Attributs steht innerhalb der Funktion ``_()``, die der Alias ist für ``gettext_lazy``. Mehr Infos in der Doku ``_ ``_ Übersetzung in den Views --------------------------------- In den Views nehmen wir den ``trans``-Tag bzw. den ``blocktranslate``-Tag, um Worte zu übersetzen. Dazu müssen die Templatetags mit ``{% load i18n %}`` erst geladen werden. .. code-block:: html+django {% load i18n %} {% trans Kategorie %} {% blocktranslate %}This string will have {{ value }} inside.{% endblocktranslate %} {% blocktranslate with event_name=event|title author_t=author|title %} This is {{ event_name }} by {{ author_t }} {% endblocktranslate %} Übersetzungsdateien erzeugen ------------------------------ Für projektweite Übersetzungen ein Verzeichnis ``locale`` unter ``event_manager`` erstellen und für die App ``events`` ebenfalls ein ``locale`` unter ``event_manager/events``. .. code-block:: bash django-admin makemessages --all Dadurch wird folgende Verzeichnis-Struktur für die ``events`` erstellt: .. code-block:: bash events .. │ ├───locale │ │ ├───de │ │ │ └───LC_MESSAGES │ │ └───en │ │ └───LC_MESSAGES │ ├───templates msgid ....... Dieses Feld ist die ID, idealerweise der Text in der Sprache, die dem ``LANGUAGE_CODE`` aus den ``settings.py`` entspricht. Der Grund ist: wenn wir für diese Sprache kein Übersetzungs-File haben, wird als Default dieser Wert genommen. msgstr ....... ``msgstr`` ist die Übersetzung in der entsprechenden Sprache. Beispiel .......... die Zeichenkette "Hallo Welt" fungiert als ``msgid`` in den **po-Dateien**. Soll für die englische Sprache eine Übersetzung eingetragen werden, wird in der englischen **po-Datei** der ``msgstr`` entsrpechend gefüllt. .. code-block:: bash x = _("Hallo Welt") in der po-Datei (EN): .. code-block:: bash #: .\events\models.py:56 msgid "Hallo Welt" msgstr "hello world" Übersetzungen in den Templates --------------------------------- .. code-block:: bash

{% trans 'Eventübersicht' %}

Po-Datei EN: .. code-block:: bash #: .\events\templates\events\category_detail.html:14 msgid "Eventübersicht" msgstr "Event Overview" Po-Datei DE: .. code-block:: bash #: .\events\templates\events\category_detail.html:14 msgid "Eventübersicht" msgstr "Übersicht der Events" Mo-Dateien erzeugen ------------------------------ Wenn die Übersetzung abgeschlossen ist, ``mo-Dateien`` erzeugen, gilt für Proekt und App .. code-block:: bash django-admin compilemessages ``django-admin compilemessages`` erstellt ``.mo-Dateien``, die laut der Django-Dokumentation binäre Dateien sind, die für die Verwendung durch gettext optimiert sind. .. admonition:: Best Practice Es sollte davon abgesehen werden, die binären ``.mo-Dateien`` in die Versionierung aufzunehmen. Anders hingegen die .po-Dateien, diese werden versioniert, da es sich um Text-Dateien handelt. Diese werden dann auf dem Zielsystem kompiliert. Der folgende Eintrag in die .gitignore-Datei ist also sinnvoll: .. code-block:: bash *.mo in beiden ``locale`` Verzeichnissen wurden jetzt die entsprechenden ``django.mo``-Dateien angelegt. Nachdem die Messages kompiliert wurden, muss der Runserver neu gestartet werden. Mit curl Accept-Language Header testen --------------------------------------- .. code-block:: bash curl -L http://127.0.0.1:8000/events/category/talk -H "Accept-Language: en" Django Shell ---------------- Wir können auch auf der Shell prüfen, ob Übersetzungen für eine Sprache vorhanden sind: .. code-block:: python from django.utils import translation translation.activate("de") translation.gettext("Event") User Sprache ändern lassen per Formular ---------------------------------------- In den ``event_manager/urls.py`` ein urlpattern hinzufügen: .. code-block:: python path('i18n/', include('django.conf.urls.i18n')), im Template ``base.html`` bauen wir jetzt noch folgendes Formular ein: .. code-block:: html+django
{% csrf_token %}
Wenn der User den Wert des Formulars ändert, findet ein Redirect auf die aktuelle Seite, allerdings in anderer Sprache statt. Dynamische Daten übersetzen ------------------------------- Bisher hatten wir nur statische Daten übersetzt. Dies geschah entweder im PythonCode, also zum Beispiel in den Models oder Views oder in den Templates per ``trans``. Hier bleiben uns zwei Möglicheiten: entsprechene Model-Felder anlegen für unterschiedliche Sprachen oder einen externen Übersetzungsdienst wie Transiflex nutzen.