angle-uparrow-clockwisearrow-counterclockwisearrow-down-uparrow-leftatcalendarcard-listchatcheckenvelopefolderhouseinfo-circlepencilpeoplepersonperson-fillperson-plusphoneplusquestion-circlesearchtagtrashx

Verfeinerung der Mehrsprachigkeit: Hinzufügen von Sprachrückwirkungen als Option

Wenn die Sprachumschaltung deaktiviert ist, wird das Element nicht angezeigt, wenn die Übersetzung nicht verfügbar ist.

11 September 2019
post main image
unsplash.com/@miabaker

In einem früheren Beitrag habe ich die erste Version der auf dieser Website verwendeten mehrsprachigen Datenbank beschrieben. Für jede Tabelle, die Felder hat, die übersetzt werden müssen, fügen wir eine "Übersetzungstabelle" mit diesen Feldern hinzu. Ich habe auch einen Sprach-Fallback implementiert: Wenn ein Element, wie ein Blog-Post, nicht in der ausgewählten Sprache existiert, wird das Element der (systemweiten) Standardsprache angezeigt. Das funktioniert gut, aber jetzt möchte ich optionalen Fallback hinzufügen, der es einigen Posts erlaubt, nur auf Spanisch zu erscheinen, anderen nur auf Französisch, etc.

Wir könnten dies einfach implementieren, indem wir die Fallback-Sprache auf eine unbekannte / nicht existierende Sprache umstellen. Wenn wir nun einen Beitrag in spanischer Sprache hinzufügen, ist der Beitrag in anderen Sprachen nicht verfügbar, genau das, was wir wollen. Das einzige Problem ist, dass wir jetzt keine Fallback-Sprache mehr haben. Ist das ein Problem?

Betrachten Sie den folgenden Fall. Wir haben einen Beitrag in Spanisch und möchten, dass der nicht existierende Beitrag in Französisch auf die Standardsprache Englisch zurückfällt. Aber wir wollen nicht, dass der nicht existierende Beitrag in Deutsch auf die Standardsprache zurückfällt. Dies ist mit der aktuellen Implementierung nicht möglich. Wie oft tritt eine solche Erkrankung auf? Nicht oft, aber ich kann mir vorstellen, dass es passieren kann, und möchte mich darauf vorbereiten. Wie setzen wir das also um?

Einige Fallback-Optionen

Für eine mehrsprachige Datenbank sind viele Fallback-Optionen möglich. Wir brauchen immer irgendwo einen Schalter, der sagt:

  • Rückgriff auf das Standard-Sprachelement, wenn keine Übersetzung für die ausgewählte Sprache vorhanden ist.
  • nicht zurückfallen, was bedeutet, dass dieser Artikel nicht in der gewählten Sprache verfügbar ist.

Für den Fallback können wir Optionen wie:

  • Fallback der Rekordstufe, z.B. Titel, Beschreibung
  • Fallback auf Feldebene z.B. Titel

Wir können es auch haben:

  • einmaliger Fallback, z.B. es gibt nur eine Fallback-Sprache
  • rekursives Fallback, z.B. wenn nicht vorhanden in portugiesischer Sprache Spanisch, wenn nicht vorhanden in spanischer Sprache Englisch

Die Rückfallsprache kann sein:

  • systemweit
  • auf Rekordniveau
  • auf Feldebene

Ohne ins Detail zu gehen, können Sie sich vorstellen, dass wir durch die Implementierung maximaler Flexibilität, aller Optionen, ein sehr komplexes System schaffen werden, das komplexe Abfragen erfordert, und dass es sogar notwendig sein kann, einen komplexen Präprozessor für das Frontend zu bauen, um langsame Seitenaufrufe aufgrund der gesamten Verarbeitung von Sprachoptionen zu verhindern.

Entscheidungen treffen

Zurück zu den Grundlagen. Ich möchte für diese Website:

  • Unterstützung mehrerer Sprachen
  • Es gibt eine systemweite Standardsprache.
  • Es gibt eine systemweite Fallback-Sprache.
  • Die Standardaktion muss ein Sprach-Fallback sein.
  • Pro Sprachelement muss es möglich sein, den Sprachfall zu deaktivieren, so dass das Element nicht verfügbar ist, wenn es nicht veröffentlicht wird.

Ich habe die folgenden Sprachen definiert:

  • language_selected: verwendete Sprache
  • language_default: die Standardsprache, eine statische systemweite Einstellung.
  • language_fallback: die Fallback-Sprache, eine dynamische systemweite Einstellung, die auf der ausgewählten Sprache basiert.

Meistens wird die Fallback-Sprache die Standardsprache sein, aber in Zukunft könnten wir diese auch in eine andere Sprache ändern:

Example#1: fr-Be Rückfall auf fr-FR

  • Standardsprache: en-US
  • gewählte Sprache: fr-BE
  • Fallback-Sprache: fr-FR

Beispiel#2: es-ES mit Rückfall auf en-GB

  • Standardsprache: en-GB
  • gewählte Sprache: es-ES
  • Fallback-Sprache: en-GB

Implementierung

Ich möchte, dass die Abfragen das System nicht verlangsamen, aber wenn dies der Fall ist, können wir jederzeit das'Zwischenspeichern von Abfrageergebnissen' und/oder die Vorverarbeitung von Abfragen hinzufügen.

Zur Zeit gibt es zwei Inhaltseintragstabellen:

  • ContentItem
  • ContentItemTranslation (Übersetzung)

Wir beginnen immer mit der Auswahl eines oder mehrerer Datensätze aus ContentItem. Wenn der ContentItemTranslation-Datensatz für die ausgewählte Sprache nicht vorhanden (oder veröffentlicht) ist, müssen wir einen Rückfall durchführen, aber nur, wenn der Rückfall für dieses Element aktiviert ist. Zeit, sich die Abfrage anzusehen, die zum Abrufen der Elemente verwendet wird. Wir unterscheiden zwei Möglichkeiten:

  • ContentItemTranslation-Datensatz existiert nicht
  • ContentItemTranslation-Datensatz vorhanden

In beiden Fällen können wir einen Beitritt von union durchführen, der erste Teil erhält den Datensatz für die ausgewählte Sprache und der zweite Teil die Sprache für die Standardsprache. Die Schwierigkeit besteht darin, wie wir den Fallback deaktivieren. Wenn der ContentItemTranslation-Datensatz existiert, können wir hier ein Kennzeichen'do_not_fallback' setzen, aber was ist, wenn der ContentItemTranslation-Datensatz nicht existiert? Dann brauchen wir irgendwo ein weiteres sprachspezifisches Flag und wieder kann dieses Flag existieren oder auch nicht.

Eine Möglichkeit, dies zu tun, besteht darin, eine weitere Tabelle ContentItemTranslationDoNotFallback mit nur einem Flag = do_not_fallback hinzuzufügen. Wie bei ContentItemTranslation können auch bei ContentItemTranslationDoNotFallback Datensätze vorhanden sein oder auch nicht. Dann haben wir drei Tische:

  • ContentItem
  • ContentItemTranslation (Übersetzung)
  • ContentItemTranslationDoNotFallback (Inhalt)

Wir möchten, dass die Standardaktion auf die Fallback-Sprache zurückfällt, wenn der'Übersetzungssatz nicht existiert'. Das alles sieht ziemlich einfach aus. Aber dies führt auch zu einem zusätzlichen Datensatz einer neuen Tabelle, die möglicherweise vorhanden ist oder nicht.

Eine weitere Möglichkeit, das zu erreichen, was wir wollen, ist, dem Übersetzungssatz das Flag'do_not_fallback' hinzuzufügen. Ich meine, wenn wir einen Datensatz zu einer neuen Tabelle ContentItemTranslationDoNotFallback hinzufügen müssen, um 'do_not_fallback' anzugeben, warum nicht den Tabelleneintrag ContentItemTranslation für diesen Zweck verwenden? Wenn es nicht existiert, fügen wir es hinzu, sonst verwenden wir es einfach.

In diesem Fall ist die Standardaktion immer noch der Rückfall in die Rückfallsprache, wenn der Datensatz "Übersetzung" nicht existiert. Wir fügen jetzt einen Übersetzungssatz ein, nicht nur, wenn wir einen Übersetzungswert haben, sondern auch, wenn wir einen Rückfall für diesen Satz wünschen. Im Übersetzungsprotokoll verwenden wir das Kennzeichen veröffentlicht / aktiv, um anzuzeigen, ob die Übersetzung existiert. Die folgende Tabelle zeigt die Aktionen für die ausgewählte Sprache.

Tabelle
InhaltEintragÜbersetzung
ContentItemTranslation
veröffentlicht
ContentItemTranslation
do_not_fallback
Aktion
1 Existiert nicht - - Kein umgerechneter Wert, Rückfall
2 Besteht Wahr Wahr oder Falsch Umgewandelten Wert verwenden
3

Besteht

Falsch Falsch Kein umgerechneter Wert, Rückfall
4 Besteht Falsch Wahr Kein umgerechneter Wert, kein Rückfall, Artikel nicht verfügbar

SQL Abfrage

Nachfolgend finden Sie eine mögliche SQL Abfrageimplementierung. Beachten Sie, dass ich 's und UNION hier verwende, die eine einfache Auswahl von Objekten bei der Verwendung einer SQLAlchemy Abfrage ermöglichen. In der aktuellen Implementierung habe ich zwei Kennzeichen, gelöscht und Status, die beide falsch sein müssen. content_item_parent_id = 0 bedeutet den Hauptübersetzungssatz.

SET @language_fallback_id = 1;
SET @language_selected_id = 2;

### [1] = fallback: translation record does not exist

(
SELECT 
  ci.id ci_id, cit.id cit_id, cit.title cit_title
FROM content_item ci, content_item_translation cit
WHERE 
  ci.content_item_type = 1
  AND ci.content_item_parent_id = 0
  AND ci.published = 1
  AND cit.content_item_id = ci.id
  AND cit.content_item_parent_id = 0
  AND cit.published = 1
  AND cit.language_id = @language_fallback_id
  AND NOT EXISTS
    (
    # return 1 when exists
    SELECT 1 
    FROM content_item_translation cit2
    WHERE 
          cit2.content_item_id = ci.id
      AND cit2.content_item_parent_id = 0
      AND cit2.language_id = @language_selected_id
      )
)

UNION ALL

### [2] = use translation: translation record exists and published = True

(
SELECT 
  ci.id ci_id, cit.id cit_id, cit.title cit_title
FROM content_item ci, content_item_translation cit
WHERE 
  ci.content_item_type = 1
  AND ci.content_item_parent_id = 0
  AND ci.published = 1
  AND cit.content_item_id = ci.id
  AND cit.content_item_parent_id = 0
  AND cit.published = 1
  AND cit.language_id = @language_selected_id
)

UNION ALL

### [3] = fallback: translation record exists and published = False & do_not_fallback = False

(
SELECT 
  ci.id ci_id, cit.id cit_id, cit.title cit_title
FROM content_item ci, content_item_translation cit
WHERE 
  ci.content_item_type = 1
  AND ci.content_item_parent_id = 0
  AND ci.published = 1
  AND cit.content_item_id = ci.id
  AND cit.content_item_parent_id = 0
  AND cit.published = 1
  AND cit.language_id = @language_fallback_id
  AND EXISTS
    (
    # return 1 when published = False & do_not_fallback = False
    SELECT 1 
    FROM content_item ci2, content_item_translation cit2
    WHERE 
          ci2.id = ci.id
      AND ci2.content_item_type = 1
      AND ci2.content_item_parent_id = 0
      AND ci2.published = 1
      AND cit2.content_item_id = ci2.id
      AND cit2.content_item_parent_id = 0
      AND cit2.published = 0
      AND cit2.do_not_fallback = 0
      AND cit2.language_id = @language_selected_id
      )
)

ORDER BY ci_id DESC;

Denn SQLAlchemy ich habe mich für den Moment entschieden, raw sql zu verwenden, um die Anzahl und die IDs für die Seitennummerierung zu erhalten, und dann diese IDs zu verwenden, um die Objekte mit einer zweiten Abfrage auszuwählen. Ja, ich bin es ein wenig leid, in SQLAlchemy die ganze Zeit zu übersetzen SQL , es braucht viel Zeit und wofür? Dennoch glaube ich, dass die Verwendung ORM von Code viel helfen kann, aber nicht immer. Es geht also darum, es richtig zu benutzen.

Administrator

Natürlich muss der Administrator den Status der Inhaltselemente einsehen können. Der Status des Inhaltselements der Übersetzung wird pro Sprache wie folgt angezeigt:

Veröffentlicht

Nicht veröffentlicht, Rückfall

Nicht veröffentlicht, kein Fallback, nicht verfügbar

Zusammenfassung

Wir haben die Inhaltselemente unserer Datenbank um die Option'do_not_fallback' erweitert. Dies führte zu mehr Komplexität, aber der zusätzliche Overhead sieht nicht wirklich massiv aus. Im Vergleich zu der Situation, in der wir immer auf die Standardsprache zurückgegriffen haben, sind die Änderungen folgende:

  • Die Anzahl der Datensätze (Positionen) ist nicht mehr die Anzahl der Datensätze für die Standardsprache.
  • Wenn wir ein Element ansehen und die Sprache auf ein Non-Fallback-Element umschalten, das keine Übersetzung hat, dann müssen wir zeigen, dass das Element nicht in der ausgewählten Sprache verfügbar ist.
  • Die Funktion "meistgesehen" (Blogbeiträge), die Funktion "Suchen" (Blogbeiträge) usw. müssen nun auch nach nicht verfügbaren Artikeln filtern.

Wieder eine Menge Arbeit, aber ich denke, das ist jetzt die mehrsprachige Funktionalität, die ich für den Rest dieses Projekts nutzen kann.

Links / Impressum

Fallback
https://www.i18next.com/principles/fallback

Language Fallback
http://sitecore-masters.com/en/language-fallback/

Multi Language i18n & Localization in Pimcore
https://pimcore.com/docs/5.x/Development_Documentation/Multi_Language_i18n/index.html

Mehr erfahren

Multilanguage

Einen Kommentar hinterlassen

Kommentieren Sie anonym oder melden Sie sich zum Kommentieren an.

Kommentare

Eine Antwort hinterlassen

Antworten Sie anonym oder melden Sie sich an, um zu antworten.