Linguist (Übersetzungs-Sync)
Die UI-Übersetzungen (lang/de.json, lang/en.json) werden über den verwalteten
Übersetzungs-Service Linguist gepflegt und mit dem Repo
synchronisiert. Übersetzer arbeiten in der Linguist-Oberfläche; ein Deploy-Command
holt die Übersetzungen kontrolliert ins Repo zurück.
Der Connector (hyperlinkgroup/linguist) round-trippt natives Laravel-lang-JSON —
Übersetzungs-Keys und Platzhalter bleiben unverändert, der App-Code (__()) bleibt
unangetastet.
Architektur in Kürze
| Eigenschaft | Wert |
|---|---|
| Verwaltete Dateien | lang/de.json, lang/en.json (Top-Level JSON-String-Keys) |
| Nicht verwaltet | lang/de/*.php, lang/en/*.php (auth/validation/…), der KI-Content-Übersetzer (DB-Inhalte) |
| Quell-/Leitsprache | Deutsch (DE) — die __()-Schlüssel sind deutsch, also ist Deutsch die Referenz |
| Zielsprache | Englisch (EN) — die Übersetzung; Linguist zeigt fehlende englische Werte in der Oberfläche |
| Laufzeit | Linguist läuft nie im Request-Pfad; __() liest die committeten lang/*.json. Laufzeit, Deploy und CI brauchen kein Token zur Laufzeit |
| Datenbank | keine — der Connector ist ein reiner Datei- + REST-API-Client |
Variablen-Round-Trip
Beim Upload werden Laravel-Platzhalter :var in das Linguist-Format {{ var }}
gewandelt; beim Download (Export mit ?prefix=:) wieder zurück nach :var. Damit
bleiben Platzhalter wie :count, :reason und das Social-Handle-Präfix
@:profileHandle byte-genau erhalten. Auch Keys mit Punkten (z. B. 1 – 3 Min.)
bleiben flache Keys (kein Nesting).
Konfiguration (.env)
# ─────────────────────────────────────────────────────────────────────────────
# Linguist (UI-Übersetzungs-Connector)
# ─────────────────────────────────────────────────────────────────────────────
# Basis-URL der Linguist-API (Cloud-Default). Self-hosted: eigene Instanz mit /vN-Suffix.
LINGUIST_URL=https://api.linguist.eu/v2
# Projekt-Slug in Linguist. Pflicht ab dem ersten Sync.
LINGUIST_PROJECT=postbox
# Bearer-API-Token (SECRET!). Nur in der .env, niemals committen.
LINGUIST_TOKEN=
# Optionale URL zur Token-Verwaltung (vom Setup-Wizard genutzt).
LINGUIST_API_TOKENS_URL=https://app.linguist.eu/settings/api-tokens
# Pull-Format: false => pretty-printed JSON (Git-freundlich), true => minified.
LINGUIST_PULL_MINIFIED=false
Das Token ist projekt-scoped; ein Wechsel auf eine self-hosted Instanz ändert nur
LINGUIST_URL.
Commands
translations:sync — deploy-sicherer Sync
Standard-Ablauf: guarded Push → Pull → Gates → Empty-Strip → Fail-safe.
php artisan translations:sync # Push (nur neue Keys) + Pull + Gates + Empty-Strip
php artisan translations:sync --no-pull # nur Push (neue Keys registrieren), lokale Dateien unangetastet
php artisan translations:sync --pull-only # nur Pull (Keys müssen bereits remote sein)
php artisan translations:sync --strict # bei Gate-Verletzung Exit 1 (Deploy abbrechen) statt Fail-safe
php artisan translations:sync --overwrite # bestehende Remote-Werte mit den lokalen überschreiben (local wins) — nur für einmalige Re-Seeds/Korrekturen
php artisan translations:sync --dry-run # nur lokale vs. remote Key-Zahlen melden
Sicherheits-Eigenschaften:
- Push ist nicht-destruktiv: lädt nur Keys hoch, die remote noch fehlen
(bestehende Übersetzungen werden nie überschrieben). Transiente Fehler (z. B. ein
HTTP 500 auf einem Batch) werden idempotent erneut versucht (
--push-attempts, Default 3) — der erneute Push überspringt bereits vorhandene Keys. - Pull läuft nur bei sauberem Push. Sonst würde der vollständige Datei-Overwrite lokale Keys verlieren, die nicht hochgeladen wurden.
- Empty-Strip: noch nicht übersetzte Keys (leerer Wert) werden aus der gepullten Datei entfernt und bleiben damit absent → sie fallen sauber auf die Quellsprache zurück, statt leer zu rendern.
- Gates: kein Re-Nesting (Dot-Keys bleiben flach), keine un-reversierten Platzhalter-Klammern, kein nicht-leerer Baseline-Key verschwindet.
- Fail-safe: schlägt ein Gate fehl, wird der committete Stand byte-genau
wiederhergestellt und der Deploy läuft mit der Baseline weiter (mit
--strictbricht er stattdessen ab).
lang:extract — CI-Guard für neue Keys
Linguist erfährt von einem neuen __('…') erst, wenn der Key in lang/<locale>.json
steht und der Push ihn hochlädt — der Connector scannt keinen Code. lang:extract
schließt diese Lücke:
php artisan lang:extract # meldet Keys, die im Code stehen, aber in en.json fehlen (Exit 1 = CI-Gate)
php artisan lang:extract --add # ergänzt die fehlenden Keys in en.json (Wert = Key)
php artisan lang:extract --locale=de
Erfasst werden einfach-gequotete statische Literale in __(), @lang(), trans()
und trans_choice(). Dynamische Keys (__($var)) und doppelt-gequotete Strings
werden bewusst nicht erfasst — diese wenigen Fälle bleiben manuell.
Workflow
Neue Übersetzungs-Keys aus dem Code
- Entwickler fügt
__('Neuer String')ein und trägt den Key inlang/en.jsonein (englischer Wert) — oder lässtlang:extract --addihn ergänzen. - Beim Deploy registriert der Push den neuen Key in Linguist (nur neue Keys).
- Übersetzer setzen den englischen Wert in der Linguist-Oberfläche.
- Der nächste Deploy holt die Übersetzung per Pull nach
lang/en.json.
Übersetzer-Änderungen
Übersetzer pflegen ausschließlich in Linguist. Lokale Wert-Änderungen an lang/*.json
sollten nicht erfolgen, da der Pull sie überschreibt — die Quelle der Übersetzungen
ist Linguist, der committete Git-Stand ist Baseline/Fallback.
Deployment (Laravel Forge)
translations:sync gehört in das Forge-Deploy-Script — nach composer install und
den Migrationen, vor dem atomaren current-Symlink-Flip:
$FORGE_PHP artisan migrate --force
$FORGE_PHP artisan translations:sync
$FORGE_PHP artisan config:cache
Wichtig bei Zero-Downtime-Deployments (Releases + current-Symlink):
lang/ist release-lokaler Code und darf nicht als „Shared/Persistent Directory" verlinkt werden (geteilt sind nurstorage/und.env). Der Pull schreibt ins neue Release, das erst beim Symlink-Flip live geht — zero-downtime-sicher.- Das Token kommt aus der geteilten
.env. - Production-
lang/*.json(nach Pull + Empty-Strip) können vom Git-Stand abweichen (Git = Baseline, Linguist = live). Das ist gewollt. Wer den Git-Stand nachziehen will, führt den Sync lokal aus und committet bewusst.
Ein automatischer, unbeaufsichtigter Pull außerhalb des Deployments (z. B. per Cron) wird nicht empfohlen, da der Pull die Dateien vollständig überschreibt.
Laravel-13-Kompatibilität
Ab hyperlinkgroup/linguist 2.0.0 wird Laravel 13 nativ unterstützt
(illuminate/contracts ^12|^13, php ^8.3) — es genügt ein normales
composer require "hyperlinkgroup/linguist:^2.0", kein Bypass/Repository-Eintrag nötig.
Historie: Die ältere
1.0-Reihe deklarierte nur^10|^11|^12; L13 wurde dort übergangsweise per Composer-package-Repository (Constraint-Override in der Root-composer.json) überbrückt. Mit 2.0 entfällt das komplett.
Fehlerbehandlung
- Ist Linguist nicht konfiguriert (
LINGUIST_PROJECT/LINGUIST_TOKENleer), wird der Sync übersprungen (Exit 0) undlang/*bleibt unverändert. - Bei Push-Fehlern wird der Pull übersprungen; bei Gate-Verletzungen greift der
Fail-safe. In beiden Fällen läuft der Deploy mit der Baseline weiter (sofern nicht
--strict). - Erfolg/Fehler werden geloggt; der Erfolgs-Check ist „alle Sprachen erfolgreich und keine Fehler", nicht eine reine Key-Zahl.