Übersetzungsvertrag
Dieses Dokument befasst sich mit dem
@phis/ui
-Bereich der Übersetzung: den rein serverseitigen Hilfsfunktionen, den Label-Sets, den Quell-
Lokalisierungen, den strukturierten Dokumenten sowie der Verarbeitung der aufgelösten Anforderungslokalisierung durch das Paket.
Persistenz, kanonische Speicherhüllen, Anbieteranfragen und Änderungsmarkierungen fallen in den Zuständigkeitsbereich von
@phis/server
(
TRANSLATIONS.md
). Die Obermenge der Sprachumgebungsfunktionen, die
validierte Teilmenge jeder Website sowie
GET /api/v1/site/locale
fallen in den Zuständigkeitsbereich von
@phis/server
(
phis-server TRANSLATIONS.md, „Locale capability“
). Wie Übersetzungen
in einem Website-Prozess zwischengespeichert werden, ist in
gateway/CACHES.md
beschrieben.
Helfer
Die Übersetzungshelfer sind ausschließlich serverbasiert und werden aus @phis/ui/server-helpers
(server-helpers/translate.ts
) exportiert:
tr(msg, params?, ctx?, format?) und trForLocale(locale, msg, params?, ctx?, format?) übersetzen eine
auf die Website bezogene Meldung.
trBulk(msgs, ctx?, format?, sourceLocale?)
und trBulkForLocale(locale, msgs, ctx?, format?, sourceLocale?)
übersetzen mehrere Nachrichten im Site-Bereich in einer einzigen Anfrage und behalten dabei deren Reihenfolge bei: eine Antwort pro Nachricht,
an ihrer jeweiligen Position. Eine leere Nachricht wird mit einem leeren Text beantwortet und nicht gesendet, sodass ein Aufrufer die
Antworten anhand des Indexes abgleichen kann (gateway/tr.ts
). Würde man dies weglassen, wie es früher bei der Stapelverarbeitung der Fall war, würden alle nachfolgenden Antworten auf
die falsche Nachricht verschoben.
trGlobal(msg, params?, ctx?, format?)
, trGlobalForLocale(...)
, trGlobalBulk(msgs, ctx?, format?)
sowie trGlobalBulkForLocale(...)
übersetzen
globale Phi-Kopien, deren Quellsprache stets en (PHI_CANONICAL_SOURCE_LOCALE
) ist. Bei einer Anfrage für en
wird der Quelltext ohne weitere Anfrage zurückgegeben.
Regeln:
Die Helfer ohne Locale lesen die Locale der Anfrage über `resolvePhiRequestLocale()` aus und die
Site aus dem auf die Anfrage beschränkten Laufzeitsatz, der durch das CMS-Root-Rendering festgelegt wird.
ctx ist standardmäßig leer. Legen Sie diesen Wert nur fest, wenn derselbe sichtbare Text in
einem anderen semantischen Kontext unterschiedlich übersetzt werden muss. Gemeinsam genutzte UI-Texte verwenden die festen Register PHI_TR_CTX_WEB_UI_LABEL
(Beschriftungen auf Steuerelementen) und PHI_TR_CTX_WEB_UI_MESSAGE (Meldungen der Benutzeroberfläche: Leerzustände,
Fehler); Modulbeschreibungen verwenden PHI_TR_CTX_MODULE_DESCRIPTION. Erstellen Sie keine standortspezifischen
Kontexte für gemeinsam genutzte UI-Texte.
Das format ist standardmäßig auf text eingestellt. Verwenden Sie html nur für bewusst übersetzbare HTML-Markups.
Bei einer fehlgeschlagenen Anfrage wird der Quelltext zurückgegeben; die Hilfsfunktionen lösen bei einem Übersetzungsfehler niemals eine Ausnahme aus.
Dynamische Werte sind niemals Bestandteil einer übersetzten Zeichenfolge: keine Namen, Benutzernamen, E-Mail-Adressen, Firmennamen,
Kundennummern, Seitennamen, URLs oder ähnliche Daten. Der Quelltext verwendet die positionsbezogenen
Platzhalter %1, %2, … (beispielsweise Login history for %1), und die Werte werden nach der
Übersetzung eingesetzt. Die Helper für einzelne Nachrichten nehmen diese als params auf; Massenausgaben und Label-Sets werden
vom Aufrufer formatiert, und Client-Komponenten formatieren bereits übersetzte Vorlagen lokal, anstatt
Labels mit Werten zu verknüpfen.
Für Freitextseiten und Website-Inhalte wird ein einzelner Aufruf von trBulk(...) gegenüber mehreren Aufrufen von tr(...) bevorzugt.
Die Übersetzung erfolgt unabhängig vom Datenquellenvertrag und vom Absenden des Formulars.
phiRuntime() (@phis/ui/server-helpers) dient den Metadaten der Laufzeit selbst – apiBaseUrl,
internalToken, siteKey, locale und fetchSiteLocaleConfig() – und nicht der Übersetzung.
Etikettensätze
Die UI-Inhalte im Besitz von Phi werden über Label-Sets (gateway/label-set.ts) geladen. Woher die Helper stammen,
hängt davon ab, wer sie importiert:
Ein Modulpaket außerhalb von
@phis/ui bezieht diese aus
@phis/ui/module/labels, niemals aus
@phis/ui/server-helpers. Der Server-Barrel enthält dieselben Namen, jedoch zusammen mit
server-helpers/cms-root
und
next/headers; eine Moduldefinition ist auch über den Browser-Graphen erreichbar, da Client-Widgets
die damit verknüpften Label-Sets rendern; werden sie also aus dem „Barrel“ bezogen, führt dies zu einer Kompilierung und anschließend zu einem
500-Fehler auf
jeder Seite der Website (siehe
THIRD_PARTY_MODULES.md).
Der eigene Servercode einer Website kann diese aus dem Verzeichnis @phis/ui/server-helpers beziehen.
Die First-Party-Module in diesem Paket importieren gateway/label-set direkt, das ebenso spezifisch ist wie
@phis/ui/module/labels.
Die Helfer:
definePhiLabelSet({ key, ctx, sourceLocale?, labels }) deklariert eine Menge. labels ordnet semantische Schlüssel dem
Standardtext zu. Ein Eintrag kann sein eigenes Register als { text, ctx } benennen; definePhiMessageLabel(text) kennzeichnet
einen Satz innerhalb einer Menge von Beschriftungen.
definePhiRuntimeModuleLabelSet(moduleDefinition, { key, ctx, labels }) deklariert eine dem Modul zugehörige Menge.
Ihr Schlüssel lautet module:<moduleId>:<key>, und ihre Quellsprache ist die sourceLocale des Moduls.
Die Funktion getPhiLabelSet(options, definition) gibt die Texte unter denselben semantischen Schlüsseln zurück. Sie sendet eine
Sammelanfrage pro Register; die positionelle Stapelverarbeitung ist ein internes Transportdetail. Die Sätze werden nicht
als Sätze zwischengespeichert; jede Nachricht durchläuft den Übersetzungscache.
Der Widget-Code liest Bezeichnungen anhand semantischer Schlüssel aus, niemals über Freitext-Abfragen oder Positionen.
Label-Sätze sind statische UI-Texte. Sie können Platzhaltervorlagen wie Edit user %1 enthalten, werden jedoch
niemals aus aufgelösten Benutzer-, Profil-, Unternehmens- oder E-Mail-Daten generiert.
In der Datenbank werden niemals Schlüssel für Label-Sets, Loader-Pfade oder Kennungen für Übersetzungsbündel gespeichert.
First-Party-Widget-Loader befinden sich unter components/widgets/label-sets/* und sind semantisch benannt
(account.ts, inspector.ts), nicht anhand numerischer IDs. Ihre Label-Typen und Standardtexte befinden sich unter
components/widgets/label-types/*; ein Loader importiert diese, anstatt sie zu wiederholen. Die Server-
Hälfte eines Widgets importiert ihren eigenen Loader; der CMS-Renderer und das Gateway verfügen über keine konkreten Widget-Labels.
Die Eigenschaft labels eines Blocks enthält die von Phi verwalteten Benutzeroberflächen- und Steuerungstexte; defaultLabels enthält lokalisierte
Standardinhalte, die nur angezeigt werden, wenn kein CMS- oder Konfigurationstext vorhanden ist. Expliziter Konfigurationstext wie beispielsweise
config.label, config.title oder config.tooltip hat Vorrang vor beiden. Ein Block ohne Text verwendet den
Typ PhiNoLabels (types/widget-runtime.ts) anstelle der Übergabe eines leeren labels-Objekts.
Semantische Befehle wie „Speichern“, „Veröffentlichen“, „Überprüfen“, „Wiederherstellen“, „Zurücksetzen“, „Neu laden“ und „Hochladen“ stammen aus dem
„common-controls“-Label-Set (components/widgets/label-sets/common-controls.ts).
Platzhalter für Website-Texte
Text, der in die Konfiguration eines Widgets geschrieben wird – sei es durch eine Voreinstellung oder durch den Ersteller –, kann einige Fakten
über die Website benennen, anstatt sie vollständig auszuschreiben (helpers/text-placeholders.ts):
Die Namen lauten {site.name}, {site.wordmark}, {year} und {site.host}. Diese Liste umfasst die gesamte
Sprache; ein Platzhalter ist kein Pfad in die Laufzeitumgebung, und ein Name, auf den keine Antwort vorliegt, bleibt bestehen, sodass
{jahr} als {jahr} dargestellt wird und als der Tippfehler erkennbar ist, der er ist.
{site.wordmark} ist die Bezeichnung, unter der sich die Website schriftlich präsentiert, und {site.name} ist der Name, unter dem sie registriert ist.
Ist keine Wortmarke festgelegt, wird die Wortmarke durch den Namen ersetzt, sodass es sich niemals um die leere Abfrage „ask“ handelt.
Die Reihenfolge lautet: maskieren, übersetzen, ausfüllen – niemals erst übersetzen und dann ausfüllen. {year} ist ein Wort, und ein maschineller
Übersetzer übersetzt Wörter: Als Ergebnis erhält man {Jahr}, worauf nichts passt. maskPhiTextPlaceholders
wandelt zunächst jeden Namen in ein %n-Token um – dasselbe Token, das dieser Vertrag bereits für Werte verwendet – und
resolvePhiTextPlaceholders füllt diese anschließend an den Stellen aus, an denen der Übersetzer sie hinterlassen hat.
Ein Text kann eigene Werte enthalten – ein Listeneintrag { text: "Site key: %1", values: [key] }.
Diese nehmen Werte von %1 bis %k an, und die maskierten Namen beginnen bei %k+1 (maskPhiTextPlaceholders(text, k)),
sodass durch eine einzige Ersetzung beide wiederhergestellt werden. Ein Wert wird niemals übersetzt, und ein Satz, der einen Wert enthält, bildet eine
Katalogzeile anstelle einer Zeile pro Site.
Das anschließende Ausfüllen ist zudem der Grund dafür, dass eine Übersetzung wiederverwendbar ist. Die gespeicherte Übersetzung ist an den
Quelltext gekoppelt, sodass © %1 %2. All rights reserved. für jede Website, die die
Voreinstellung übernimmt, eine Zeile pro Sprachversion darstellt – und dieselbe Zeile gilt auch nach Neujahr.
Eine Voreinstellung löst beim Aufbau ihres Baums niemals einen Platzhalter auf. Dadurch werden der Name einer Website und das
Jahr der Übernahme in die Kopie jedes Nutzers geschrieben, wo sie verbleiben.
Widgets, die kurze Konfigurationstexte enthalten, lösen diese auf; lange Markdown- und HTML-Inhalte tun dies bewusst
nicht, da es sich bei einer geschweiften Klammer dort eher um ein Code-Beispiel als um einen Platzhalter handelt.
Die Ant Design-eigenen Textelemente (Paginierung, Leerstände, Auswähltext, Validierungsvorlagen, Schaltflächen in Modalfenstern) sowie
die Datums- und Zeitformatierung stammen aus der Ant Design-Lokalisierung und der gemeinsamen Day.js-Lokalisierungsbrücke
(components/root/phi-dayjs-locale.tsx), niemals aus Label-Sets. Die Ant-Design-Locale wird einmalig
im Root-ConfigProvider (components/root/phi-root-layout.tsx) angewendet. helpers/antd-locale.ts ordnet die
aufgelöste Site-Locale einem Ant-Design-Locale-Schlüssel zu und lädt sie über eine explizite Whitelist von
import("antd/locale/<key>")-Loadern, niemals über einen berechneten Importpfad.
Ländereinstellung anfordern
Die aufgelöste Locale-Anfrage stammt von @phis/server (GET /api/v1/site/locale) und enthält die Parameter
locale, language, die optionalen Parameter script und region, direction, intlLocale, den optionalen Parameter
deeplTargetLang, labelFallbacks sowie source (helpers/site-locale-config.ts). Die gemeinsame Benutzeroberfläche überprüft nicht,
ob die Plattform die jeweilige Locale unterstützt, und implementiert weder DeepL-Zielsprachen noch die Textrichtung oder Fallback-Ketten
neu.
Root, CMS, Metadaten, Übersetzung und Weiterleitungscode verwenden alle dasselbe, auf die Anfrage bezogene Ergebnis
(server-helpers/request-locale.ts); keines davon wendet eine eigene Locale-Priorität an. Diese Anfrage ist
no-store, da die Antwort von der Sitzung, den Cookies, der explizit festgelegten Locale und dem
Accept-Language-Header abhängt. Die Locale-Konfiguration der Website (fetchSiteLocaleConfig) kann pro Website zwischengespeichert werden.
x-locale enthält ausschließlich die explizit angegebene Locale einer öffentlichen Route mit Locale-Präfix. Der Site-Proxy
(next/site-proxy.ts) entfernt alle eingehenden x-locale-Angaben und setzt diesen Wert ausschließlich anhand des Pfadpräfixes; ein Cookie,
eine Browsereinstellung, ein Profilwert oder eine bereits aufgelöste Locale werden niemals in diesen Wert übernommen.
Übersetzungsaufrufe auf Site-Ebene enthalten keine Quell-Locale der Site: @phis/server
leitet diese von der Site ab.
Die defaultLocale
der Site ist die letzte Ausweichoption für die Anfrage-Locale und wird niemals als Ersatz für die englische (en
) Quelle globaler Texte herangezogen.
Transport der Quell-Lokalisierung
Bei gewöhnlichen Site-Übersetzungsaufrufen wird sourceLocale weggelassen; @phis/server ermittelt die unveränderliche Site-Quell-
Locale.
Modultext, der einen Übersetzungsaufruf der Website erreicht, enthält die sourceLocale des Moduls: eine Navigationsbezeichnung,
die ein Modul bereitstellt (PhiCmsNavigationLabel.sourceLocale, festgelegt beim Auflösen des Eintrags), sowie den
Titel einer Modulroute. Ohne diese Angabe liest eine Website, deren Quellsprache nicht die des Moduls ist, die
englische Version des Moduls als ihre eigene und übersetzt sie niemals. Eine Bezeichnung, die der Betreiber umbenennt oder hinzufügt, verliert dieses Feld oder verfügt gar nicht erst darüber
und wird in der Quellsprache der Website gelesen.
Eigenständige globale Bezeichnungen können sourceLocale angeben; wird dies nicht angegeben, gilt Englisch. Modul-eigene Voreinstellungen von Drittanbietern
übernehmen die Spracheinstellung des zugehörigen Moduls, anstatt eine andere Ausgangssprache anzugeben.
Jedes Laufzeitmodul deklariert eine kanonische sourceLocale; wird diese nicht angegeben, gilt Englisch. Alle vom Paket erstellten
Komponenteninhalte erben diese über den „Module Label-Set“-Helper. Komponentenspezifische Überschreibungen der Quellsprache
sowie mehrsprachige Modul-Metadaten sind ungültig.
Von Phi verwaltete Module erstellen die kanonische Version in englischer Sprache. Metadaten und Komponentenbezeichnungen von Modulen von Drittanbietern
können eine andere deklarierte Modul-Quellsprache verwenden.
Externe Markdown- und HTML-Widgets speichern in der Widget-Konfiguration eine einzige „plainsourceLocale“-Einstellung. Diese kann
von den auf der Website verfügbaren Ziellokalisierungen abweichen.
sourceLocale wird als eigenständiges Anfragefeld weitergeleitet. Es darf keinesfalls in ctx kodiert werden.
Kann der aktive Anbieter die konfigurierte externe Quelle nicht übersetzen, zeigt das Widget das Originaldokument an
und führt bei jeder Darstellung keine erneute Erkennung durch.
Kontext und Format
Aufrufer stellen lediglich optionale Kontextinformationen in natürlicher Sprache bereit. Sie erstellen keine internen Kontext-Flags.
Das Gateway leitet das format: "html" sowie explizite Metadaten externer Quellen weiter; @phis/server ist für die
kanonischen Speicherhüllen [html], [extern] und [html:extern] verantwortlich.
Strukturierte Dokumente
Markdown und HTML werden vor dem Aufruf des Gateways in semantische Übersetzungseinheiten zerlegt.
Eine Überschrift, ein Absatz, ein Listenpunkt-Absatz, ein Zitatblock-Absatz oder eine Tabellenzelle gilt als eine Einheit.
Inline-Formatierungen und sichtbarer Linktext bleiben durch übersetzungssicheres HTML-Markup erhalten.
Code, Inline-Code und Linkziele werden niemals übersetzt. Der Alternativtext von Bildern ist übersetzbar.
Alle Einheiten aus einem Dokument nutzen das Bulk-Gateway. Der Server wertet Cache-Treffer und die Aufteilung der Anforderung durch den Anbieter aus,
wobei die Reihenfolge der Einheiten beibehalten wird.
Die HTML-Sanitisierung orientiert sich an Vertrauensgrenzen und nicht an der Häufigkeit der Darstellung. Kanonisches Inline-HTML wird
vor der Speicherung durch @phis/server sanitisiert. Externe Dokumente und von Anbietern erstelltes HTML werden
im Server-Rendering-Pfad bereinigt, da es sich dabei nicht um vertrauenswürdiges, dauerhaft gespeichertes Widget-Markup handelt. Öffentliche und
App-HTML-Widget-Clients erhalten sicheres, serialisiertes Markup und dürfen den HTML-Sanitizer nicht importieren.
In intern gespeicherten Markdown-/HTML-Dateien eingebettete Ziele für „Typed Page“ und „Asset“ sowie die zwingende
Ablehnung dieser Ziele in externen Dokumenten richten sich nach
der Datei REFERENCES.md. Referenz-
Identitäten und -ziele sind strukturelle Metadaten und niemals Übersetzungseinheiten.
Bei externen Quellen ermittelt der Builder die Quellsprache einmalig, sobald ein neuer Anhang konfiguriert wird,
und speichert diese Angabe im Rahmen der normalen, generischen Widget-Konfigurationsaktualisierung. Bei nachfolgenden Darstellungen wird dieser Wert verwendet.
Der Builder kann diesen Wert explizit überschreiben oder nach einer Änderung des Anhangs eine erneute Ermittlung anfordern.
Das Entfernen eines Widgets oder das Löschen seiner URL trennt lediglich die Quelle. Es darf keine impliziten Anweisungen zum Löschen von Übersetzungseinheiten auslösen,
da Einheiten möglicherweise gemeinsam genutzt werden und Builder-Vorgänge weiterhin rückgängig gemacht werden können müssen.
Titel, Beschreibung und andere vom Paket erstellte Bezeichnungen des Laufzeitmoduls verwenden globale Übersetzungen, niemals
Website-Übersetzungseinheiten. Server-eigene Autorierungskataloge gruppieren Metadaten nach der Quellsprache des Moduls, übersetzen diese
in großen Mengen und senden lokalisierte Kopien an Client-Anbieter. Die unveränderliche Definition des Laufzeitmoduls
behält ihre kanonischen Quellzeichenfolgen und die Quellsprache bei; die Lokalisierung darf den aktiven Modulkatalog nicht überschreiben.
Ein Modul, dessen Quell-Locale nicht die kanonische ist, wird von jeder Installation, auf der es ausgeführt wird,
auf dem eigenen Provider dieser Installation übersetzt und niemals über das CDN. Das kanonische Englisch, nach dem seine Einheit indiziert ist,
wird generiert und nicht deklariert, sodass zwei Installationen dieselbe Zeichenfolge unter zwei unterschiedlichen Hashes indizieren
und kein Mirror auf eine der beiden reagieren kann. Was diese Zeichenfolgen gemeinsam nutzbar machen würde, ist ein Paket, das
seinen kanonischen Text neben der eigenen Sprache deklariert; eine lokale Vorübersetzung ist nicht dasselbe, auch wenn
es so aussieht, als wäre es so
(
phis-server TRANSLATIONS.md, „Übersetzungsmodi“).
Modulkategorie, ID, Anbieterschlüssel, Funktionen und andere Systemidentitäten bleiben unveränderliche Schlüssel. Ein
separates globales Kategorie-Label-Set kann den Benutzern einen Kategorieschlüssel anzeigen. Optionale, von der Website verwaltete Darstellungsüberschreibungen
sind Website-Inhalte und ersetzen nicht die globale Paketübersetzung.