Dokumentation durchblättern
Module entwickeln
Übersetzungen
Wie ein Modul Übersetzungsdateien anlegt, Schlüssel statt Texte an Registries übergibt, marken-neutrale Texte schreibt und eigene UI-Sprachen über die LocaleRegistry registriert.
Der Kern bringt von Haus aus Deutsch (de) und Englisch (en) mit. Ihr Modul
legt seine eigenen Texte daneben und kann — seit 1.1.3 — über die LocaleRegistry sogar
eine ganz neue Oberflächensprache ergänzen, ohne den Kern anzufassen.
Übersetzungsdateien im Modul
Die Übersetzungen eines Moduls liegen in dessen lang/-Verzeichnis, nach Sprachcode
getrennt:
modules/my-module/
lang/
de/
messages.php
en/
messages.php
Dateiformat
Es sind gewöhnliche Laravel-Übersetzungs-Arrays — nichts Modul-Spezifisches. Wer Laravel kennt, kann sofort loslegen:
<?php
// lang/de/messages.php
return [
'settings' => 'Einstellungen',
'upload' => 'Hochladen',
'document_types' => [
'contract' => 'Vertrag',
'invoice' => 'Rechnung',
'other' => 'Sonstiges',
],
];
<?php
// lang/en/messages.php
return [
'settings' => 'Settings',
'upload' => 'Upload',
'document_types' => [
'contract' => 'Contract',
'invoice' => 'Invoice',
'other' => 'Other',
],
];
Übersetzungen verwenden
Jeder Modul-Text ist mit dem Modul-Slug namespaced — also {slug}::datei.schlüssel. Den
Namespace registriert der ModuleManager während des Bootens automatisch; Sie müssen
loadTranslationsFrom() für die normalen Modul-Texte nicht selbst aufrufen.
// In PHP
__('my-module::messages.settings'); // → 'Einstellungen' (wenn Locale de ist)
// Mit Parametern
__('my-module::messages.uploaded_by', ['name' => 'Max']);
{{-- In Blade --}}
{{ __('my-module::messages.settings') }}
@lang('my-module::messages.upload')
Nicht beim Booten übersetzen — Schlüssel an Registries übergeben
Das ist die wichtigste Regel auf dieser Seite. __() löst immer gegen die aktuelle
Sprache auf — und zwar in dem Moment, in dem der Aufruf läuft. Während Ihr
ServiceProvider::boot() ausgeführt wird, ist aber nur die Fallback-Sprache (de) aktiv.
Die Sprache pro Benutzer oder pro Kunde wird erst später, je Request, gesetzt. Ein __()
in boot() friert sein Ergebnis also auf Deutsch ein.
Das fällt überall dort auf die Füße, wo Sie einen Wert an eine Registry übergeben, den der Kern später rendert — zum Beispiel ein Menü-Label:
// ❌ Friert das Menü-Label auf Deutsch ein — der Seiteninhalt übersetzt, das Menü nicht.
$nav->addItem(label: __('my-module::messages.nav_label'), ...);
// ✅ Den rohen Schlüssel übergeben; die NavigationRegistry löst ihn pro Request auf.
$nav->addItem(label: 'my-module::messages.nav_label', ...);
Faustregel: In boot() geben Sie Übersetzungsschlüssel an Navigations- und
Portal-Registries weiter — die lösen verzögert, zur Render-Zeit, auf. __() rufen Sie nur
dort auf, wo der Wert direkt verbraucht wird: in Blade-Views, Controllern oder in
Closures, die je Request laufen. Mehr dazu unter
Navigation und Dashboard.
Verschachtelte Schlüssel
Verschachtelte Arrays sprechen Sie mit Punkt-Notation an:
__('my-module::messages.document_types.contract'); // → 'Vertrag'
Pluralformen
Für Mengenangaben nutzen Sie Laravels Plural-Syntax und trans_choice():
// lang/de/messages.php
return [
'documents_count' => '{0} Keine Dokumente|{1} Ein Dokument|[2,*] :count Dokumente',
];
// Verwendung
trans_choice('my-module::messages.documents_count', 5); // → '5 Dokumente'
Marken-neutrale Texte mit brand()
Der Kern setzt einen BrandedTranslator ein, der Marken-Bezeichnungen automatisch ersetzt.
Das ist der Grund, warum Sie den Produktnamen in Texten nicht fest verdrahten sollten:
Schreiben Sie stattdessen den Platzhalter :brand, dann zeigt derselbe Text je nach
Installation die richtige Marke an.
// Nicht so:
return ['welcome' => 'Willkommen bei Schneespur'];
// Sondern so:
return ['welcome' => 'Willkommen bei :brand'];
// Verwendung:
__('my-module::messages.welcome', ['brand' => brand()]);
Lokalisierte module.json
Auch die Felder name und description in der module.json lassen sich lokalisieren —
nützlich, damit Ihr Modul in der Modul-Verwaltung in der passenden Sprache erscheint:
{
"name": {
"de": "Dokumentenverwaltung",
"en": "Document Management"
},
"description": {
"de": "Verwaltet Verträge und Dokumente für Kunden.",
"en": "Manages contracts and documents for customers."
}
}
Die Methode pickLocalized() des Module-Modells wählt die aktuelle App-Sprache aus und
fällt auf Englisch zurück, wenn die gewünschte Sprache fehlt.
Eine eigene Sprache registrieren (Sprachpakete)
Seit 1.1.3 kann ein Modul eine neue Oberflächensprache ergänzen — über die
LocaleRegistry (ein Singleton). Der Kern registriert de und en; Ihr Modul fügt
weitere hinzu. So lässt sich etwa Tschechisch (cs) nachrüsten, ohne eine einzige
Core-Datei zu ändern.
Schritt 1 — den Sprachcode registrieren, in Ihrem ServiceProvider::boot():
use App\Services\Extension\LocaleRegistry;
public function boot(): void
{
app(LocaleRegistry::class)->add('cs', 'Čeština');
// Die eigenen Texte des Moduls (namespaced):
$this->loadTranslationsFrom(__DIR__ . '/../lang', 'my-module');
}
Sobald die Sprache registriert ist, wird cs überall akzeptiert, wo eine Sprache geprüft
oder angewendet wird — bei der Kunden- und Benutzer-Validierung, in der
EnsureCustomer/EnsureDriver-Middleware, in den Auswahllisten der Verwaltung und im
Installer. Diese Stellen fragen LocaleRegistry::codes() bzw. has() ab, statt einer fest
verdrahteten de,en-Liste. Genau deshalb genügt das eine add() — Sie müssen nicht an
zehn Stellen nachtragen.
Schritt 2 — die Kern-Oberfläche übersetzen. Die Modul-namespaced Texte
(my-module::…) decken nur die Bildschirme Ihres Moduls ab. Um auch die
Anwendungs-Oberfläche (Verwaltung, Portal, Fahrer-Ansicht) in die neue Sprache zu
bringen, müssen die Kern-Sprachdateien unter lang/<code>/ vorliegen — in denselben
Namespaces wie lang/de/. Laravel löst Kern-Texte aus dem Wurzel-Namespace auf; ein
Modul-Namespace überschreibt diese nicht. Dafür gibt es zwei Wege:
- Das Sprachpaket-Modul kopiert seine
lang/cs/-Dateien bei der Aktivierung in das Kern-Verzeichnislang/der Installation. - Die
lang/cs/-Dateien werden direkt im Kern mitgeliefert.
Zum Boot-Zeitpunkt: Modul-Sprachen werden während ModuleManager::boot() registriert,
das innerhalb des app()->booted()-Callbacks läuft. Die anwendungsweite default_locale
wird daher nach dem Booten der Module angewendet — eine reine cs-Installation löst
dadurch korrekt auf.
Die genaue API der LocaleRegistry steht unter
Registries.
Sprache pro Benutzer, Fahrer und Kunde
Die Oberflächensprache lässt sich einem einzelnen Akteur zuweisen; er sieht die Anwendung nach dem Login in dieser Sprache.
| Akteur | Spalte | Wo gesetzt | Wo angewendet |
|---|---|---|---|
| Kunde | customers.locale | Portal-Profil (Selbstbedienung) oder Kundenformular in der Verwaltung | EnsureCustomer-Middleware (pro Request) |
| Fahrer / Verwalter | users.locale (nullable) | Verwaltung → Benutzerformular | EnsureDriver-Middleware (pro Request) |
Ein null in users.locale bedeutet „die anwendungsweite Standardsprache verwenden”.
Beide Middlewares wenden die Sprache nur an, wenn LocaleRegistry::has($code) zutrifft —
eine nicht (mehr) registrierte Sprache fällt sicher auf den Standard zurück. Die
<select>-Listen der Sprachauswahl werden aus LocaleRegistry::labels() gebaut, sodass
jede registrierte Sprache automatisch erscheint.
Ein praktisches Beispiel: eine deutsche Schneespur-Installation mit einem tschechischen
Fahrer. Der Betreiber installiert das tschechische Sprachpaket und setzt die Sprache dieses
Fahrers auf cs. Der Fahrer meldet sich an und sieht die gesamte Fahrer-Oberfläche auf
Tschechisch, während für alle anderen Deutsch bleibt.
Anwendungsweite Standardsprache
Die Standardsprache der Anwendung liegt in der Einstellung default_locale (Verwaltung →
Unternehmenseinstellungen) und akzeptiert jede registrierte Sprache. Sie wird im
app()->booted()-Callback des AppServiceProvider angewendet — nachdem die Module ihre
Sprachen registriert haben, abgesichert durch LocaleRegistry::has().
Ist der Standard eine andere Sprache als de, löst die Marke zu Wintertrace auf (siehe
das Zwei-Marken-Modell unter Architektur). Eine
vollständig tschechische Installation wird dadurch automatisch als Wintertrace gebrandet.