Englische Fassung der Markenseiten: Merkmale, Leistungen, Abschnitte, Showroom, Bildunterschriften
Diese Markenfelder waren nur einsprachig gespeichert und erschienen auf der englischen Seite deutsch. Neue Spalte cms_brands.field_translations (additiv) trägt die Übersetzungen je Sprache, die bisherigen Spalten bleiben die deutsche Fassung; ohne Übersetzung gilt weiter Deutsch. Marken-Editor bearbeitet die Felder über den DE/EN-Umschalter (Bilder und Rubriken bleiben sprachübergreifend). BrandTranslationsSeeder spielt die englischen Texte der vier Kernmarken ein, ohne gepflegte Übersetzungen zu überschreiben. php artisan cms:untranslated listet Inhalte, die in einer Sprache noch fehlen oder deutsch sind. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
dc31cdb401
commit
8ddc85422e
8 changed files with 982 additions and 34 deletions
|
|
@ -28,6 +28,15 @@ class CmsBrand extends Model
|
|||
/** @var list<string> */
|
||||
public const GALLERY_CATEGORIES = [self::GALLERY_PROJECT, self::GALLERY_INSPIRATION, self::GALLERY_COMPANY];
|
||||
|
||||
/** Sprache der Spalten highlights, services, sections, showroom und der Bildunterschriften. */
|
||||
public const BASE_LOCALE = 'de';
|
||||
|
||||
/**
|
||||
* Felder, die nur einsprachig als Spalte existieren und ihre Übersetzungen in `field_translations`
|
||||
* tragen (09.10.2026). `gallery_captions` sind die Bildunterschriften, nach Bild-Datei.
|
||||
*/
|
||||
public const LOCALIZED_FIELDS = ['highlights', 'services', 'sections', 'showroom', 'gallery_captions'];
|
||||
|
||||
protected $table = 'cms_brands';
|
||||
|
||||
protected $fillable = [
|
||||
|
|
@ -46,6 +55,7 @@ class CmsBrand extends Model
|
|||
'cta_title',
|
||||
'cta_text',
|
||||
'gallery',
|
||||
'field_translations',
|
||||
'website_url',
|
||||
'showroom',
|
||||
'origin_city',
|
||||
|
|
@ -77,6 +87,7 @@ class CmsBrand extends Model
|
|||
'services' => 'array',
|
||||
'sections' => 'array',
|
||||
'gallery' => 'array',
|
||||
'field_translations' => 'array',
|
||||
'is_published' => 'boolean',
|
||||
'order' => 'integer',
|
||||
];
|
||||
|
|
@ -359,6 +370,87 @@ class CmsBrand extends Model
|
|||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
/**
|
||||
* Ein einsprachiges Feld in der gewünschten Sprache: die Übersetzung aus `field_translations`,
|
||||
* sonst die deutsche Spalte. `gallery_captions` liefert Bild-Datei => Unterschrift.
|
||||
*/
|
||||
public function localized(string $field, ?string $locale = null): mixed
|
||||
{
|
||||
$locale ??= app()->getLocale();
|
||||
$base = $field === 'gallery_captions'
|
||||
? collect(self::normalizeGallery($this->gallery))->mapWithKeys(fn (array $item) => [$item['image'] => $item['caption']])->all()
|
||||
: $this->getAttribute($field);
|
||||
|
||||
if ($locale === self::BASE_LOCALE) {
|
||||
return $base;
|
||||
}
|
||||
|
||||
$translated = $this->field_translations[$locale][$field] ?? null;
|
||||
|
||||
if ($field === 'gallery_captions') {
|
||||
foreach ($base as $image => $caption) {
|
||||
if (filled($translated[$image] ?? null)) {
|
||||
$base[$image] = (string) $translated[$image];
|
||||
}
|
||||
}
|
||||
|
||||
return $base;
|
||||
}
|
||||
|
||||
return filled($translated) ? $translated : $base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Schreibt ein einsprachiges Feld: Deutsch in die Spalte, andere Sprachen nach `field_translations`.
|
||||
* Eine Übersetzung, die genau dem deutschen Stand entspricht, wird nicht gespeichert – sonst
|
||||
* gälte deutscher Text als übersetzt.
|
||||
*/
|
||||
public function setLocalized(string $field, string $locale, mixed $value): void
|
||||
{
|
||||
if ($locale === self::BASE_LOCALE) {
|
||||
if ($field !== 'gallery_captions') {
|
||||
$this->setAttribute($field, $value);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$translations = $this->field_translations ?? [];
|
||||
$base = $this->localized($field, self::BASE_LOCALE);
|
||||
|
||||
if ($field === 'gallery_captions') {
|
||||
$value = array_filter(
|
||||
(array) $value,
|
||||
fn (mixed $caption, string $image): bool => filled($caption) && $caption !== ($base[$image] ?? null),
|
||||
ARRAY_FILTER_USE_BOTH,
|
||||
);
|
||||
}
|
||||
|
||||
if (blank($value) || $value === $base) {
|
||||
unset($translations[$locale][$field]);
|
||||
} else {
|
||||
$translations[$locale][$field] = $value;
|
||||
}
|
||||
|
||||
$translations = array_filter($translations, fn (mixed $fields): bool => filled($fields));
|
||||
$this->field_translations = $translations === [] ? null : $translations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bildergalerie in der gewünschten Sprache (Bilder und Rubriken sind sprachunabhängig).
|
||||
*
|
||||
* @return list<array{image: string, category: string, caption: string}>
|
||||
*/
|
||||
public function localizedGallery(?string $locale = null): array
|
||||
{
|
||||
$captions = $this->localized('gallery_captions', $locale);
|
||||
|
||||
return array_map(
|
||||
fn (array $item): array => [...$item, 'caption' => (string) ($captions[$item['image']] ?? $item['caption'])],
|
||||
self::normalizeGallery($this->gallery),
|
||||
);
|
||||
}
|
||||
|
||||
public function toFrontendArray(): array
|
||||
{
|
||||
return [
|
||||
|
|
@ -368,18 +460,18 @@ class CmsBrand extends Model
|
|||
'tagline' => $this->tagline,
|
||||
'teaser' => $this->teaser,
|
||||
'summary' => $this->summary,
|
||||
'highlights' => $this->highlights ?? [],
|
||||
'highlights' => $this->localized('highlights') ?? [],
|
||||
'description' => $this->description,
|
||||
'logo' => $this->logo,
|
||||
'image' => $this->image,
|
||||
'services' => $this->services ?? [],
|
||||
'sections' => self::normalizeSections($this->sections),
|
||||
'services' => $this->localized('services') ?? [],
|
||||
'sections' => self::normalizeSections($this->localized('sections')),
|
||||
'widget_label' => (string) $this->widget_label,
|
||||
'cta_title' => (string) $this->cta_title,
|
||||
'cta_text' => (string) $this->cta_text,
|
||||
'gallery' => self::normalizeGallery($this->gallery),
|
||||
'gallery' => $this->localizedGallery(),
|
||||
'website_url' => $this->website_url,
|
||||
'showroom' => $this->showroom,
|
||||
'showroom' => $this->localized('showroom'),
|
||||
'origin_city' => (string) $this->origin_city,
|
||||
'origin_distance' => (string) $this->origin_distance,
|
||||
'origin_role' => (string) $this->origin_role,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue