From bfad13af7dd9dd682f88142d9598929b59289f7e Mon Sep 17 00:00:00 2001 From: Kevin Adametz Date: Mon, 6 Jul 2026 13:36:23 +0000 Subject: [PATCH] Spoke-Frontend-Audit: Fake-Inhalte raus, Newsletter-Rueckbau, echte Boersendaten, EN-Lokalisierung Frontend-Umbau (Kevin, 06.07.2026), Ergebnis des businessportal24-Audits: - Fake-Anzeigen und Mock-/Fallback-Daten entfernt (focus-ad, feed-ad, highlight-card, press-release-card, featured-releases, press-releases-grid); Sektionen rendern nur noch mit echten Daten oder gar nicht - Newsletter-Strip und Newsletter-Box zurueckgebaut (Feature nachgelagert, vor Reaktivierung DOI-Flow noetig) - Echte Boersendaten: MarketDataService (Yahoo-Finance-Quotes, config/market.php, Cache) + market:refresh alle 15 Min. im Scheduler; live-ticker blendet sich ohne Daten aus - EN-Lokalisierung der Kern-Seiten via lang/de/web.php + lang/en/web.php; Carbon-Locale folgt app()->getLocale() - Web-Tests auf den neuen Frontend-Stand nachgezogen (Suite: 993 gruen) Co-Authored-By: Claude Fable 5 --- app/Console/Commands/RefreshMarketData.php | 36 ++ app/Services/Web/HomePageData.php | 21 +- app/Services/Web/MarketDataService.php | 109 +++++++ app/Services/Web/TickerData.php | 42 +++ app/Support/WebPortalContext.php | 18 +- config/domains.php | 16 +- config/market.php | 40 +++ lang/de/pricing.php | 2 +- lang/de/web.php | 307 ++++++++++++++++++ lang/en/web.php | 302 +++++++++++++++++ .../views/components/emails/layout.blade.php | 2 +- .../press-release-submit-modal.blade.php | 2 +- .../components/web/active-newsrooms.blade.php | 33 +- .../views/components/web/feed-ad.blade.php | 52 --- .../views/components/web/feed-item.blade.php | 53 +-- .../components/web/feed-top-item.blade.php | 48 +-- .../views/components/web/focus-ad.blade.php | 50 --- .../views/components/web/focus-hero.blade.php | 100 ++---- .../components/web/focus-side-item.blade.php | 38 +-- .../components/web/hero-fallback.blade.php | 20 ++ .../components/web/highlight-card.blade.php | 88 ----- .../components/web/industry-index.blade.php | 8 +- .../web/industry-spotlight.blade.php | 145 +++------ .../components/web/live-ticker.blade.php | 177 +++++----- .../views/components/web/most-read.blade.php | 23 +- .../components/web/newsletter-strip.blade.php | 76 ----- .../views/components/web/pagination.blade.php | 6 +- .../web/press-release-card.blade.php | 159 --------- .../components/web/pricing-teaser.blade.php | 24 +- .../components/web/publish-band.blade.php | 55 ++++ .../components/web/publisher-cta.blade.php | 10 +- .../components/web/quality-summary.blade.php | 10 +- .../components/web/site-footer.blade.php | 35 +- .../components/web/site-header.blade.php | 66 ++-- .../livewire/admin/payments/plans.blade.php | 2 +- .../admin/press-releases/create.blade.php | 2 +- .../admin/press-releases/edit.blade.php | 2 +- .../admin/press-releases/index.blade.php | 2 +- .../admin/press-releases/show.blade.php | 2 +- .../livewire/customer/dashboard.blade.php | 4 +- .../customer/press-kits/create.blade.php | 2 +- .../customer/press-kits/index.blade.php | 2 +- .../customer/press-releases/create.blade.php | 8 +- .../customer/press-releases/edit.blade.php | 8 +- .../customer/press-releases/index.blade.php | 6 +- .../customer/press-releases/show.blade.php | 2 +- .../livewire/web/featured-releases.blade.php | 101 ------ .../livewire/web/press-release-feed.blade.php | 50 +-- .../web/press-releases-grid.blade.php | 187 ----------- resources/views/livewire/web/search.blade.php | 41 ++- .../views/web/businessportal24.blade.php | 15 +- resources/views/web/faq.blade.php | 2 +- resources/views/web/kategorie.blade.php | 87 ++--- .../views/web/layouts/web-master.blade.php | 6 +- resources/views/web/newsroom.blade.php | 51 +-- resources/views/web/newsrooms.blade.php | 54 +-- resources/views/web/preise.blade.php | 4 +- resources/views/web/presseecho.blade.php | 15 +- resources/views/web/pressekonto.blade.php | 19 +- resources/views/web/release-detail.blade.php | 130 +++----- resources/views/web/suche.blade.php | 12 +- .../views/web/veroeffentlichen.blade.php | 15 +- routes/console.php | 12 + .../Feature/Web/Businessportal24HomeTest.php | 82 ++++- tests/Feature/Web/EditionRoutingTest.php | 16 + tests/Feature/Web/MarketDataTest.php | 98 ++++++ tests/Feature/Web/PresseechoHomeTest.php | 44 ++- 67 files changed, 1735 insertions(+), 1521 deletions(-) create mode 100644 app/Console/Commands/RefreshMarketData.php create mode 100644 app/Services/Web/MarketDataService.php create mode 100644 app/Services/Web/TickerData.php create mode 100644 config/market.php create mode 100644 lang/de/web.php create mode 100644 lang/en/web.php delete mode 100644 resources/views/components/web/feed-ad.blade.php delete mode 100644 resources/views/components/web/focus-ad.blade.php create mode 100644 resources/views/components/web/hero-fallback.blade.php delete mode 100644 resources/views/components/web/highlight-card.blade.php delete mode 100644 resources/views/components/web/newsletter-strip.blade.php delete mode 100644 resources/views/components/web/press-release-card.blade.php create mode 100644 resources/views/components/web/publish-band.blade.php delete mode 100644 resources/views/livewire/web/featured-releases.blade.php delete mode 100644 resources/views/livewire/web/press-releases-grid.blade.php create mode 100644 tests/Feature/Web/MarketDataTest.php diff --git a/app/Console/Commands/RefreshMarketData.php b/app/Console/Commands/RefreshMarketData.php new file mode 100644 index 00000000..9946c458 --- /dev/null +++ b/app/Console/Commands/RefreshMarketData.php @@ -0,0 +1,36 @@ +info('Marktdaten sind deaktiviert (MARKET_DATA_ENABLED=false).'); + + return self::SUCCESS; + } + + $slides = $marketData->refresh(); + + if ($slides === []) { + $this->warn('Keine Kursdaten erhalten — Cache unverändert.'); + + return self::FAILURE; + } + + foreach ($slides as $slide) { + $this->line(sprintf('%s: %s (%s)', $slide['label'], $slide['value'], $slide['delta'])); + } + + return self::SUCCESS; + } +} diff --git a/app/Services/Web/HomePageData.php b/app/Services/Web/HomePageData.php index 2f0c1376..cd2aba23 100644 --- a/app/Services/Web/HomePageData.php +++ b/app/Services/Web/HomePageData.php @@ -9,12 +9,11 @@ use App\Models\PressRelease; use App\Support\EditorialClock; use Illuminate\Database\Eloquent\Builder; use Illuminate\Support\Carbon; -use Illuminate\Support\Str; /** * Datenaufbereitung der öffentlichen Startseite einer Ausgabe: Lead-/Seiten-/ * meistgelesene Meldungen, aktive Newsrooms, Branchen-Index, „Heute im Fokus" - * (Spotlight), Ticker und Kennzahlen. + * (Spotlight) und Kennzahlen. Der Ad-Hoc-Ticker kommt zentral aus TickerData. */ class HomePageData { @@ -207,26 +206,15 @@ class HomePageData $spotlight = [ 'industry' => $spotlightTranslation->name, 'stats' => [ - ['label' => 'Meldungen (7 Tage)', 'value' => number_format($windowCount, 0, ',', '.'), 'sub' => 'Stand '.$referenceNow->translatedFormat('j. M Y')], - ['label' => 'Aktive Unternehmen', 'value' => number_format($activeCompanies, 0, ',', '.'), 'sub' => 'in dieser Branche'], - ['label' => 'Im Archiv', 'value' => number_format($archiveCount, 0, ',', '.'), 'sub' => 'Meldungen gesamt'], + ['label' => __('web.spotlight.stat_window'), 'value' => number_format($windowCount, 0, ',', '.'), 'sub' => __('web.spotlight.stat_window_sub', ['date' => $referenceNow->translatedFormat('j. M Y')])], + ['label' => __('web.spotlight.stat_companies'), 'value' => number_format($activeCompanies, 0, ',', '.'), 'sub' => __('web.spotlight.stat_companies_sub')], + ['label' => __('web.spotlight.stat_archive'), 'value' => number_format($archiveCount, 0, ',', '.'), 'sub' => __('web.spotlight.stat_archive_sub')], ], 'releases' => $spotlightReleases->all(), ]; } } - // Ad-Hoc-Ticker aus den jüngsten echten Pressemitteilungen. - $tickerItems = $publishedQuery() - ->orderByDesc('published_at') - ->limit(8) - ->get(['id', 'title', 'published_at']) - ->map(static fn (PressRelease $release): array => [ - 'time' => $release->published_at?->format('H:i') ?? '', - 'text' => Str::limit((string) $release->title, 70), - ]) - ->all(); - return [ 'leadRelease' => $leadRelease, 'sideReleases' => $sideReleases, @@ -234,7 +222,6 @@ class HomePageData 'activeNewsrooms' => $activeNewsrooms, 'industryIndex' => $industryIndex, 'spotlight' => $spotlight, - 'tickerItems' => $tickerItems, 'homeStats' => [ 'publishedCount' => $publishedQuery()->count(), 'publishedToday' => $publishedQuery()->whereDate('published_at', $referenceNow->toDateString())->count(), diff --git a/app/Services/Web/MarketDataService.php b/app/Services/Web/MarketDataService.php new file mode 100644 index 00000000..99d9f252 --- /dev/null +++ b/app/Services/Web/MarketDataService.php @@ -0,0 +1,109 @@ + + */ + public function slides(): array + { + if (! config('market.enabled')) { + return []; + } + + /** @var list $slides */ + $slides = Cache::get((string) config('market.cache_key'), []); + + return is_array($slides) ? $slides : []; + } + + /** + * @return list + */ + public function refresh(): array + { + $slides = []; + + foreach ((array) config('market.symbols', []) as $config) { + $quote = $this->fetchQuote((string) $config['symbol']); + + if ($quote === null) { + continue; + } + + [$price, $previousClose] = $quote; + + $deltaPercent = $previousClose > 0.0 + ? ($price - $previousClose) / $previousClose * 100 + : 0.0; + + $slides[] = [ + 'label' => (string) $config['label'], + 'value' => number_format($price, (int) ($config['precision'] ?? 2), ',', '.').($config['suffix'] ?? ''), + 'delta' => ($deltaPercent >= 0 ? '+' : '').number_format($deltaPercent, 1, ',', '.').'%', + 'positive' => $deltaPercent >= 0, + ]; + } + + if ($slides === []) { + Log::warning('market:refresh lieferte keine Kursdaten — bestehender Cache-Stand bleibt erhalten.'); + + return $this->slides(); + } + + Cache::put((string) config('market.cache_key'), $slides, (int) config('market.cache_ttl', 86400)); + + return $slides; + } + + /** + * @return array{0: float, 1: float}|null [Kurs, Vortagesschluss] + */ + private function fetchQuote(string $symbol): ?array + { + try { + $response = Http::timeout(10) + ->withHeaders(['User-Agent' => 'Mozilla/5.0 (compatible; PressekontoTicker/1.0)']) + ->get(config('market.endpoint').rawurlencode($symbol), [ + 'range' => '1d', + 'interval' => '1d', + ]); + } catch (\Throwable $exception) { + Log::warning('Kursabruf fehlgeschlagen.', ['symbol' => $symbol, 'error' => $exception->getMessage()]); + + return null; + } + + if (! $response->successful()) { + Log::warning('Kursabruf fehlgeschlagen.', ['symbol' => $symbol, 'status' => $response->status()]); + + return null; + } + + $meta = $response->json('chart.result.0.meta'); + + $price = $meta['regularMarketPrice'] ?? null; + $previousClose = $meta['chartPreviousClose'] ?? $meta['previousClose'] ?? null; + + if (! is_numeric($price) || ! is_numeric($previousClose)) { + return null; + } + + return [(float) $price, (float) $previousClose]; + } +} diff --git a/app/Services/Web/TickerData.php b/app/Services/Web/TickerData.php new file mode 100644 index 00000000..c03cda0b --- /dev/null +++ b/app/Services/Web/TickerData.php @@ -0,0 +1,42 @@ + + */ + public function items(Portal $portal, string $language): array + { + $cacheKey = sprintf('web.ticker.%s.%s', $portal->value, $language); + + return Cache::remember($cacheKey, self::CACHE_TTL_SECONDS, function () use ($portal, $language): array { + return PressRelease::query() + ->forPortalValues([$portal->value, Portal::Both->value]) + ->forLanguage($language) + ->visibleInArchive() + ->orderByDesc('published_at') + ->limit(8) + ->get(['id', 'title', 'published_at']) + ->map(static fn (PressRelease $release): array => [ + 'time' => $release->published_at?->format('H:i') ?? '', + 'text' => Str::limit((string) $release->title, 70), + ]) + ->all(); + }); + } +} diff --git a/app/Support/WebPortalContext.php b/app/Support/WebPortalContext.php index 2cba8dc1..8aab4dc4 100644 --- a/app/Support/WebPortalContext.php +++ b/app/Support/WebPortalContext.php @@ -23,13 +23,21 @@ class WebPortalContext public readonly array $domainConfig, ) {} + /** + * Portal-Ableitung ohne Seiteneffekte (kein Anwenden der Domain-Config) — + * für Komponenten, die nur wissen müssen, auf welchem Portal sie rendern. + */ + public static function portalFromHost(): Portal + { + return str_contains(request()->getHost(), 'presseecho') + ? Portal::Presseecho + : Portal::Businessportal24; + } + public static function fromRequest(): self { - $host = request()->getHost(); - - [$domainKey, $portal] = str_contains($host, 'presseecho') - ? ['presseecho', Portal::Presseecho] - : ['businessportal24', Portal::Businessportal24]; + $portal = self::portalFromHost(); + $domainKey = $portal === Portal::Presseecho ? 'presseecho' : 'businessportal24'; return new self( domainKey: $domainKey, diff --git a/config/domains.php b/config/domains.php index 6f1041d1..5a7df9f2 100644 --- a/config/domains.php +++ b/config/domains.php @@ -57,11 +57,11 @@ return [ 'report_email' => env('REPORT_EMAIL_PRESSEKONTO'), 'umami_website_id' => 'ca440b65-a5bd-42c1-8cdb-96f34734003c', 'tagline_short' => 'Publisher · Hub', - 'tagline_long' => 'Der gemeinsame Publisher-Bereich für presseecho und businessportal24 – Pressemitteilungen schreiben, redaktionell prüfen lassen, auf beiden Reichweiten veröffentlichen.', + 'tagline_long' => 'Der gemeinsame Publisher-Bereich für presseecho und businessportal24 – Pressemitteilungen schreiben, automatisiert prüfen lassen, auf beiden Reichweiten veröffentlichen.', 'footer_legal' => '© :year pressekonto · Alle Rechte vorbehalten', 'about_label' => 'Über pressekonto', 'meta_title' => 'pressekonto – Publisher-Hub für presseecho und businessportal24', - 'meta_description' => 'Ein Konto, zwei Reichweiten: Pressemitteilungen redaktionell geprüft auf presseecho und businessportal24 gleichzeitig veröffentlichen.', + 'meta_description' => 'Ein Konto, zwei Reichweiten: Pressemitteilungen qualitätsgeprüft auf presseecho und businessportal24 gleichzeitig veröffentlichen.', ], ], @@ -85,16 +85,16 @@ return [ 'report_email' => env('REPORT_EMAIL_PRESSEECHO'), 'umami_website_id' => '3ea8a7b0-105d-4519-b200-138cadc0fbdc', 'tagline_short' => 'Pressemitteilungen · DACH', - 'tagline_long' => 'Fachmeldungen aus Wirtschaft und Industrie. Redaktionell geprüft. Strukturiert distribuiert.', + 'tagline_long' => 'Fachmeldungen aus Wirtschaft und Industrie. Qualitätsgeprüft. Strukturiert distribuiert.', 'footer_legal' => '© :year presseecho · Alle Rechte vorbehalten', 'about_label' => 'Über presseecho', 'meta_title' => 'Aktuelle Pressemitteilungen aus Wirtschaft & Industrie – presseecho', - 'meta_description' => 'Redaktionell kuratierte Pressemitteilungen aus Wirtschaft und Industrie im DACH-Raum.', + 'meta_description' => 'Qualitätsgeprüfte Pressemitteilungen aus Wirtschaft und Industrie im DACH-Raum.', 'newsletter_topics' => [ ['name' => 'Tageszusammenfassung', 'meta' => 'Mo–Fr · 07:00 Uhr', 'active' => true], ['name' => 'Wochenrückblick', 'meta' => 'Sonntags · 18:00 Uhr', 'active' => false], ['name' => 'Energie & Klima', 'meta' => 'ab Content-Score 80 · max. 2× pro Woche', 'active' => true], - ['name' => 'Industrie & Forschung', 'meta' => 'ausgewählt von der Redaktion · ca. 3–5 Mails/Monat', 'active' => false], + ['name' => 'Industrie & Forschung', 'meta' => 'kuratierte Auswahl · ca. 3–5 Mails/Monat', 'active' => false], ], ], ], @@ -119,16 +119,16 @@ return [ 'report_email' => env('REPORT_EMAIL_BUSINESSPORTAL'), 'umami_website_id' => 'f03a5fc3-ac44-488f-9118-84004861099f', 'tagline_short' => 'Pressemitteilungen · DACH', - 'tagline_long' => 'Veröffentlichungs-Portal für redaktionell geprüfte Pressemitteilungen aus Deutschland, Österreich und der Schweiz.', + 'tagline_long' => 'Veröffentlichungs-Portal für qualitätsgeprüfte Pressemitteilungen aus Deutschland, Österreich und der Schweiz.', 'footer_legal' => '© :year businessportal24 · Alle Rechte vorbehalten', 'about_label' => 'Über businessportal24', 'meta_title' => 'Aktuelle Pressemitteilungen aus der deutschen Wirtschaft – businessportal24', - 'meta_description' => 'Pressemitteilungen aus Deutschland, Österreich und der Schweiz. Redaktionell geprüft. Strukturiert distribuiert.', + 'meta_description' => 'Pressemitteilungen aus Deutschland, Österreich und der Schweiz. Qualitätsgeprüft. Strukturiert distribuiert.', 'newsletter_topics' => [ ['name' => 'Tageszusammenfassung', 'meta' => 'Mo–Fr · 07:00 Uhr', 'active' => true], ['name' => 'Wochenrückblick', 'meta' => 'Sonntags · 18:00 Uhr', 'active' => false], ['name' => 'Energie & Klima', 'meta' => 'ab Content-Score 80 · max. 2× pro Woche', 'active' => true], - ['name' => 'IPO & M&A', 'meta' => 'ausgewählt von der Redaktion · ca. 3–5 Mails/Monat', 'active' => false], + ['name' => 'IPO & M&A', 'meta' => 'kuratierte Auswahl · ca. 3–5 Mails/Monat', 'active' => false], ], ], ], diff --git a/config/market.php b/config/market.php new file mode 100644 index 00000000..792dcd3f --- /dev/null +++ b/config/market.php @@ -0,0 +1,40 @@ + env('MARKET_DATA_ENABLED', true), + + 'cache_key' => 'web.market-data', + + // Cache-Lebensdauer in Sekunden. Bewusst großzügig (24 h), damit ein + // vorübergehender API-Ausfall die Anzeige nicht sofort leert; der + // Scheduler aktualisiert deutlich häufiger. + 'cache_ttl' => 86400, + + 'endpoint' => env('MARKET_DATA_ENDPOINT', 'https://query1.finance.yahoo.com/v8/finance/chart/'), + + /* + | label — Anzeigename im Ticker + | symbol — Yahoo-Finance-Symbol + | precision — Nachkommastellen der Kursanzeige + | suffix — optionales Einheiten-Suffix (z. B. " $") + */ + 'symbols' => [ + ['label' => 'DAX', 'symbol' => '^GDAXI', 'precision' => 0, 'suffix' => ''], + ['label' => 'EUR/USD', 'symbol' => 'EURUSD=X', 'precision' => 2, 'suffix' => ''], + ['label' => 'Brent Öl', 'symbol' => 'BZ=F', 'precision' => 2, 'suffix' => ' $'], + ], + +]; diff --git a/lang/de/pricing.php b/lang/de/pricing.php index 636ccfbc..bd308d45 100644 --- a/lang/de/pricing.php +++ b/lang/de/pricing.php @@ -110,7 +110,7 @@ return [ 'notes' => [ 'slots' => [ 'title' => 'Slots und Kontingente', - 'text' => 'Ihr Monatskontingent (Slots) gilt für den jeweiligen Abrechnungsmonat; nicht genutzte Slots verfallen mit Monatsende. Slots sind kein Geldguthaben. Ein Slot wird nur durch eine tatsächliche Veröffentlichung verbraucht — abgelehnte Mitteilungen kosten keinen Slot; schlägt eine Veröffentlichung durch einen technischen Fehler bei uns fehl, wird der Slot automatisch zurückgebucht. Tageslimits schützen die redaktionelle Qualität der Portale; darüber hinaus eingereichte Mitteilungen können für den Folgetag eingeplant werden.', + 'text' => 'Ihr Monatskontingent (Slots) gilt für den jeweiligen Abrechnungsmonat; nicht genutzte Slots verfallen mit Monatsende. Slots sind kein Geldguthaben. Ein Slot wird nur durch eine tatsächliche Veröffentlichung verbraucht — abgelehnte Mitteilungen kosten keinen Slot; schlägt eine Veröffentlichung durch einen technischen Fehler bei uns fehl, wird der Slot automatisch zurückgebucht. Tageslimits schützen die inhaltliche Qualität der Portale; darüber hinaus eingereichte Mitteilungen können für den Folgetag eingeplant werden.', ], 'credits' => [ 'title' => 'Credits', diff --git a/lang/de/web.php b/lang/de/web.php new file mode 100644 index 00000000..3bbb99cf --- /dev/null +++ b/lang/de/web.php @@ -0,0 +1,307 @@ + [ + 'home_title' => 'Aktuelle Pressemitteilungen aus Wirtschaft & Industrie – :brand', + 'home_description' => 'Pressemitteilungen aus Deutschland, Österreich und der Schweiz. Qualitätsgeprüft. Strukturiert distribuiert.', + ], + + 'shared' => [ + 'yesterday' => 'Gestern', + 'read_time' => ':minutes Min. Lesezeit', + 'minutes_short' => ':minutes Min.', + 'recommendation' => 'Empfehlung', + ], + + 'countries' => [ + 'de' => 'Deutschland', + 'at' => 'Österreich', + 'ch' => 'Schweiz', + ], + + 'header' => [ + 'edition' => 'Ausgabe', + 'pricing' => 'Preise', + 'homepage' => 'Startseite', + 'login' => 'Anmelden', + 'publish' => 'Veröffentlichen', + 'publish_aria' => 'Pressemitteilung veröffentlichen', + 'search_label' => 'Pressemitteilungen, Unternehmen, Branchen, ISIN durchsuchen', + 'search_placeholder' => 'Pressemitteilungen, Unternehmen, Branchen, ISIN…', + 'search_open' => 'Suche öffnen', + 'search_close' => 'Suche schließen', + 'search_eyebrow' => 'Suche', + 'search_title' => 'Pressemitteilungen durchsuchen', + 'search_submit' => 'Suchen', + 'advanced_search' => 'Erweiterte Suche', + 'press' => 'Drücken Sie', + 'to_close' => 'zum Schließen', + ], + + 'nav' => [ + 'home' => 'Startseite', + 'menu' => 'Menü', + 'aria_label' => 'Hauptnavigation', + 'open_menu' => 'Hauptmenü öffnen', + 'section' => 'Rubrik', + ], + + 'ticker' => [ + 'aria_label' => 'Ad-Hoc-Ticker', + 'sr_intro' => 'Aktuelle Ad-Hoc-Meldungen:', + ], + + 'hero' => [ + 'title' => 'Im Fokus', + 'clock_format' => 'dddd, HH:mm [Uhr]', + 'editorial_pick' => 'Top-Auswahl', + 'top_story' => 'Top-Meldung', + 'sidebar_title' => 'Was sonst zählt', + ], + + 'feed' => [ + 'title' => 'Aktuelle Meldungen', + 'filter_all' => 'Alle', + 'filter_today' => 'Heute', + 'filter_week' => 'Diese Woche', + 'empty' => 'Aktuell liegen keine Meldungen vor. Sobald Pressemitteilungen veröffentlicht werden, erscheinen sie hier.', + 'more' => 'Weitere :count Meldungen anzeigen', + ], + + 'most_read' => [ + 'title' => 'Meistgelesen', + ], + + 'newsrooms' => [ + 'title' => 'Aktive Newsrooms', + 'today' => 'Heute', + 'count_suffix' => 'Meldungen letzte 7 Tage', + 'active_today' => 'heute aktiv', + 'all' => 'Alle Newsrooms anzeigen', + ], + + 'spotlight' => [ + 'title' => 'Heute im Fokus', + 'stat_window' => 'Meldungen (7 Tage)', + 'stat_window_sub' => 'Stand :date', + 'stat_companies' => 'Aktive Unternehmen', + 'stat_companies_sub' => 'in dieser Branche', + 'stat_archive' => 'Im Archiv', + 'stat_archive_sub' => 'Meldungen gesamt', + ], + + 'industry_index' => [ + 'title' => 'Branchen-Index', + 'window' => 'Letzte 7 Tage', + 'releases' => 'Meldungen', + ], + + 'publisher_cta' => [ + 'eyebrow' => 'Für Unternehmen', + 'title' => 'Veröffentlichen Sie Ihre Pressemitteilung', + 'subline' => 'Reichweite in DACH · Inhaltsprüfung · Strukturierte Distribution', + 'submit' => 'Jetzt einreichen', + 'pricing' => 'Tarife & Pakete ansehen', + ], + + 'publish_band' => [ + 'eyebrow' => 'Für Unternehmen', + 'title' => 'Ihre Pressemitteilung, qualitätsgeprüft veröffentlicht', + 'subline' => 'Anmelden, Meldung einreichen, veröffentlichen — mit Reichweite im DACH-Raum, eigenem Newsroom und dauerhaft auffindbarem Archiv.', + 'cta_submit' => 'Pressemitteilung veröffentlichen', + 'cta_pricing' => 'Tarife & Pakete', + 'footnote' => 'Ohne Abo-Zwang · Inhaltsprüfung werktags innerhalb 24 h', + 'step1_title' => 'Konto anlegen', + 'step1_text' => 'In rund zwei Minuten — ein Konto für alle Portale, Anmeldung wahlweise passwortlos per Magic-Link.', + 'step2_title' => 'Meldung einreichen', + 'step2_text' => 'Text, Bild und Ansprechpartner hochladen — die automatisierte Inhaltsprüfung läuft direkt mit.', + 'step3_title' => 'Veröffentlichen', + 'step3_text' => 'Nach bestandener Inhaltsprüfung live — inklusive Newsroom, Kategorien-Zuordnung und Archiv.', + ], + + 'pricing_teaser' => [ + 'title' => 'Tarife & Pakete', + 'details' => 'Alle Details', + 'popular' => 'Beliebt', + 'month' => 'Monat', + 'year' => 'Jahr', + 'two_months_free' => '2 Monate gratis', + 'quota' => 'Pressemitteilungen / Monat', + 'daily_limit' => 'max. pro Tag', + 'no_daily_limit' => 'kein Tageslimit', + 'select' => 'Auswählen', + 'footnote' => 'Alle Preise zzgl. MwSt. · Jederzeit kündbar · Auch ohne Abo als Einzelveröffentlichung möglich.', + 'publish_direct' => 'Direkt veröffentlichen', + ], + + 'quality' => [ + 'eyebrow' => 'Unser Qualitätsstandard', + 'claim' => 'Alle Pressemitteilungen durchlaufen eine automatisierte Inhaltsprüfung.', + 'detail' => 'Mindestqualität gewährleistet durch unseren Content-Score — eine automatische Bewertung von Quellenqualität, Verifizierungsstatus und inhaltlicher Relevanz.', + 'guidelines' => 'Veröffentlichungsrichtlinien', + ], + + 'footer' => [ + 'sections' => 'Rubriken', + 'newsrooms' => 'Newsrooms', + 'for_companies' => 'Für Unternehmen', + 'publish' => 'Veröffentlichen', + 'pricing' => 'Tarife & Pakete', + 'company' => 'Unternehmen', + 'contact' => 'Kontakt', + 'help' => 'Hilfe-Center', + 'cancellation' => 'Verträge hier kündigen', + 'imprint' => 'Impressum', + 'privacy' => 'Datenschutz', + 'terms' => 'AGB', + 'guidelines' => 'Richtlinien', + 'report' => 'Inhalte melden', + ], + + 'search' => [ + 'meta_title' => 'Pressemitteilungen durchsuchen – :brand', + 'meta_description' => 'Durchsuchen Sie das geprüfte Pressemitteilungs-Archiv nach Stichwort, Unternehmen und Branche – qualitätsgeprüft und dauerhaft auffindbar im DACH-Raum.', + 'eyebrow' => 'Archiv-Suche', + 'intro' => 'Stichwort, Unternehmen oder Branche – durchsuchen Sie das vollständige, qualitätsgeprüfte Archiv.', + 'placeholder' => 'Suchbegriff, Unternehmen oder Branche…', + 'searching' => 'sucht…', + 'reset' => 'Suche zurücksetzen', + 'all_categories' => 'Alle Rubriken', + 'results' => 'Ergebnis|Ergebnisse', + 'for_term' => 'für „:term"', + 'sort' => 'Sortierung', + 'sort_newest' => 'Neueste zuerst', + 'sort_oldest' => 'Älteste zuerst', + 'no_results' => 'Keine Treffer', + 'no_results_text' => 'Für Ihre Suche gibt es derzeit keine Pressemitteilungen. Versuchen Sie einen anderen Begriff oder entfernen Sie Filter.', + 'pagination' => 'Seitennummerierung', + 'previous_page' => 'Vorherige Seite', + 'next_page' => 'Nächste Seite', + 'discover_title' => 'Durchsuchen Sie das Archiv', + 'discover_text' => ':count geprüfte Pressemitteilungen — durchsuchbar nach Stichwort, Unternehmen und Branche. Geben Sie oben einen Suchbegriff ein oder wählen Sie eine Rubrik.', + 'popular_categories' => 'Beliebte Rubriken', + ], + + 'newsrooms_page' => [ + 'meta_title' => 'Newsrooms – :brand', + 'meta_description' => 'Alle aktiven Unternehmens-Newsrooms im Überblick – qualitätsgeprüfte Pressemitteilungen je Unternehmen, gebündelt und dauerhaft auffindbar im DACH-Raum.', + 'eyebrow' => 'Unternehmens-Newsrooms', + 'intro' => 'Alle aktiven Unternehmen mit ihren qualitätsgeprüften Pressemitteilungen – gebündelt an einem Ort.', + 'stat_total_sub' => 'mit veröffentlichten Meldungen', + 'stat_active' => 'Aktiv (30 Tage)', + 'stat_archive' => 'Meldungen im Archiv', + 'stat_archive_sub' => 'qualitätsgeprüft', + 'search_label' => 'Newsroom suchen', + 'search_placeholder' => 'Unternehmen suchen…', + 'reset_filter' => 'Filter zurücksetzen', + 'latest_release' => 'Letzte Meldung :date', + 'days_30' => '30 Tage', + 'none_found' => 'Keine Newsrooms gefunden', + 'none_found_term' => 'Für „:term" gibt es derzeit keinen passenden Newsroom. Versuchen Sie einen anderen Suchbegriff.', + 'none_available' => 'Aktuell sind keine aktiven Newsrooms verfügbar.', + 'cta_eyebrow' => 'Eigener Newsroom', + 'cta_title' => 'Bündeln Sie Ihre Pressemitteilungen im eigenen Newsroom.', + 'cta_text' => 'Eigene Marken-URL · alle Meldungen zentral · SEO-Vorteil für Ihr Unternehmen · qualitätsgeprüft.', + ], + + 'newsroom_page' => [ + 'meta_description' => 'Newsroom von :company: alle qualitätsgeprüften Pressemitteilungen gebündelt an einem Ort.', + 'back_aria' => 'Zurück zu allen Newsrooms', + 'all_newsrooms' => 'Alle Newsrooms', + 'recent_stat' => ':count Meldungen in den letzten 30 Tagen', + 'since' => 'Newsroom seit :year', + 'industries' => 'Branchen', + 'last_30_days' => 'Letzte 30 Tage', + 'published_releases' => 'veröffentlichte Meldungen', + 'total_hits' => 'Aufrufe gesamt', + 'total_hits_sub' => 'über alle Meldungen', + 'latest' => 'Aktuellste Meldung', + 'empty' => 'Aktuell keine weiteren Meldungen in diesem Newsroom.', + 'own_title' => 'Eigenen Newsroom starten', + 'own_text' => 'Bündeln Sie alle Ihre Pressemitteilungen unter einer eigenen Marken-URL – mit Logo, Branchen-Zuordnung und SEO-Vorteil.', + 'own_cta' => 'Newsroom anlegen', + ], + + 'category' => [ + 'fallback_name' => 'Branche', + 'default_description' => 'Aktuelle Pressemitteilungen aus dem Bereich :category – qualitätsgeprüft und strukturiert distribuiert im DACH-Raum.', + 'meta_title' => 'Pressemitteilungen :category – :brand', + 'eyebrow' => 'Branche', + 'back_aria' => 'Zurück zu allen Meldungen', + 'all_releases' => 'Alle Meldungen', + 'month_stat' => ':count Meldungen in den letzten 30 Tagen · :today am :date', + 'sub_topics' => 'Sub-Themen', + 'stat_today' => 'Meldungen aktueller Tag', + 'vs_previous_week' => 'ggü. Vorwoche', + 'last_7_days' => 'in den letzten 7 Tagen', + 'more_top' => 'Weitere Top-Meldungen', + 'no_more_top' => 'Aktuell keine weiteren Top-Meldungen.', + 'no_more_releases' => 'Aktuell keine weiteren Meldungen in dieser Branche.', + 'week_stat' => ':count Meldungen in den letzten 7 Tagen', + 'search_archive' => 'Im Archiv suchen', + 'empty_title' => 'Noch keine Meldungen in :category', + 'empty_text' => 'Sobald Pressemitteilungen in dieser Branche veröffentlicht werden, erscheinen sie hier.', + 'sub_industries' => 'Sub-Branchen', + 'volume_30_days' => 'Volumen letzte 30 Tage', + 'trend_7_days' => '7-Tage-Trend', + 'related' => 'Verwandte Branchen', + 'cta_eyebrow' => 'Für Unternehmen aus :category', + 'cta_title' => 'Veröffentlichen Sie Ihre Pressemitteilung gezielt in :category.', + 'cta_text' => 'Reichweite über aktive Newsrooms · Strukturierte Distribution im DACH-Raum · Qualitätsgeprüft.', + 'cta_submit' => 'Pressemitteilung einreichen', + 'cta_pricing' => 'Tarife & Branchen-Pakete ansehen', + ], + + 'detail' => [ + 'breadcrumbs' => 'Brotkrumen', + 'press_release' => 'Pressemitteilung', + 'embargo_lifted' => 'Embargo aufgehoben', + 'audience' => 'Für Wirtschaftsjournalisten', + 'time_at' => ':time Uhr', + 'words' => ':count Wörter', + 'recommended' => 'Empfehlung', + 'recommended_hint' => 'Diese Pressemitteilung erreicht einen hohen Content-Score — basierend auf Quellenqualität, Verifizierungsstatus und inhaltlicher Relevanz.', + 'image_placeholder' => 'Bild · Pressefoto', + 'image_placeholder_company' => 'Bild · Pressefoto :company', + 'photo' => 'Foto', + 'about_company' => 'Über das Unternehmen', + 'press_contact' => 'Pressekontakt', + 'verified_hint' => 'Verifizierter Publisher — Identität und Domain bestätigt', + 'verified_by' => 'Identität und Domain durch :brand bestätigt', + 'verified_publisher' => 'Verifizierter Publisher', + 'published_by' => 'Veröffentlicht von', + 'to_newsroom' => 'Zum Newsroom', + 'share' => 'Teilen', + 'email' => 'E-Mail', + 'copy_link' => 'Link kopieren', + 'copied' => 'Kopiert!', + 'keywords' => 'Schlagwörter', + 'further' => 'Weiterführend', + 'print' => 'Drucken', + 'report_release' => 'Pressemitteilung melden', + 'privacy_rights' => 'Datenschutz & Persönlichkeitsrecht', + 'legal_question' => 'Sie haben ein rechtliches Anliegen zu dieser Mitteilung?', + 'report_content' => 'Inhalt melden', + 'remove_data' => 'Eigene Daten entfernen', + 'reports_free' => 'Meldungen sind kostenlos.', + 'more_from' => 'Mehr von :company', + 'more_from_sub' => 'Aktuelle Mitteilungen aus diesem Newsroom', + 'all_from' => 'Alle Mitteilungen von :company', + 'related' => 'Verwandte Meldungen', + 'related_sub' => 'Reaktionen, ähnliche Themen, Branchenkontext', + 'all_from_category' => 'Alle Meldungen aus :category', + ], + +]; diff --git a/lang/en/web.php b/lang/en/web.php new file mode 100644 index 00000000..2c37c643 --- /dev/null +++ b/lang/en/web.php @@ -0,0 +1,302 @@ + [ + 'home_title' => 'Latest press releases from business & industry – :brand', + 'home_description' => 'Press releases from Germany, Austria and Switzerland. Quality-checked. Distributed in a structured way.', + ], + + 'shared' => [ + 'yesterday' => 'Yesterday', + 'read_time' => ':minutes min read', + 'minutes_short' => ':minutes min', + 'recommendation' => 'Recommended', + ], + + 'countries' => [ + 'de' => 'Germany', + 'at' => 'Austria', + 'ch' => 'Switzerland', + ], + + 'header' => [ + 'edition' => 'Edition', + 'pricing' => 'Pricing', + 'homepage' => 'Homepage', + 'login' => 'Sign in', + 'publish' => 'Publish', + 'publish_aria' => 'Publish a press release', + 'search_label' => 'Search press releases, companies, industries, ISIN', + 'search_placeholder' => 'Press releases, companies, industries, ISIN…', + 'search_open' => 'Open search', + 'search_close' => 'Close search', + 'search_eyebrow' => 'Search', + 'search_title' => 'Search press releases', + 'search_submit' => 'Search', + 'advanced_search' => 'Advanced search', + 'press' => 'Press', + 'to_close' => 'to close', + ], + + 'nav' => [ + 'home' => 'Home', + 'menu' => 'Menu', + 'aria_label' => 'Main navigation', + 'open_menu' => 'Open main menu', + 'section' => 'Section', + ], + + 'ticker' => [ + 'aria_label' => 'Ad-hoc ticker', + 'sr_intro' => 'Latest ad-hoc announcements:', + ], + + 'hero' => [ + 'title' => 'In Focus', + 'clock_format' => 'dddd, HH:mm', + 'editorial_pick' => 'Top selection', + 'top_story' => 'Top story', + 'sidebar_title' => 'Also worth noting', + ], + + 'feed' => [ + 'title' => 'Latest releases', + 'filter_all' => 'All', + 'filter_today' => 'Today', + 'filter_week' => 'This week', + 'empty' => 'There are no releases at the moment. As soon as press releases are published, they will appear here.', + 'more' => 'Show :count more releases', + ], + + 'most_read' => [ + 'title' => 'Most read', + ], + + 'newsrooms' => [ + 'title' => 'Active newsrooms', + 'today' => 'Today', + 'count_suffix' => 'releases in the last 7 days', + 'active_today' => 'active today', + 'all' => 'View all newsrooms', + ], + + 'spotlight' => [ + 'title' => 'In focus today', + 'stat_window' => 'Releases (7 days)', + 'stat_window_sub' => 'As of :date', + 'stat_companies' => 'Active companies', + 'stat_companies_sub' => 'in this industry', + 'stat_archive' => 'In the archive', + 'stat_archive_sub' => 'releases in total', + ], + + 'industry_index' => [ + 'title' => 'Industry index', + 'window' => 'Last 7 days', + 'releases' => 'releases', + ], + + 'publisher_cta' => [ + 'eyebrow' => 'For companies', + 'title' => 'Publish your press release', + 'subline' => 'Reach across the DACH region · Quality check · Structured distribution', + 'submit' => 'Submit now', + 'pricing' => 'View plans & packages', + ], + + 'publish_band' => [ + 'eyebrow' => 'For companies', + 'title' => 'Your press release, quality-checked and published', + 'subline' => 'Sign up, submit your release, publish — with reach across the DACH region, your own newsroom and a permanently searchable archive.', + 'cta_submit' => 'Publish a press release', + 'cta_pricing' => 'Plans & pricing', + 'footnote' => 'No subscription required · Quality check within 24 h on business days', + 'step1_title' => 'Create an account', + 'step1_text' => 'Takes about two minutes — one account for all portals, optionally passwordless via magic link.', + 'step2_title' => 'Submit your release', + 'step2_text' => 'Upload text, image and press contact — an automated quality check runs alongside.', + 'step3_title' => 'Publish', + 'step3_text' => 'Live after passing the quality check — including newsroom, category mapping and archive.', + ], + + 'pricing_teaser' => [ + 'title' => 'Plans & packages', + 'details' => 'All details', + 'popular' => 'Popular', + 'month' => 'month', + 'year' => 'year', + 'two_months_free' => '2 months free', + 'quota' => 'press releases / month', + 'daily_limit' => 'max. per day', + 'no_daily_limit' => 'no daily limit', + 'select' => 'Select', + 'footnote' => 'All prices excl. VAT · Cancel anytime · Single releases available without a subscription.', + 'publish_direct' => 'Publish directly', + ], + + 'quality' => [ + 'eyebrow' => 'Our quality standard', + 'claim' => 'Every press release goes through a quality check.', + 'detail' => 'Minimum quality ensured by our content score — an automated assessment of source quality, verification status and content relevance.', + 'guidelines' => 'Publishing guidelines', + ], + + 'footer' => [ + 'sections' => 'Sections', + 'newsrooms' => 'Newsrooms', + 'for_companies' => 'For companies', + 'publish' => 'Publish', + 'pricing' => 'Plans & packages', + 'company' => 'Company', + 'contact' => 'Contact', + 'help' => 'Help center', + 'cancellation' => 'Cancel contracts here', + 'imprint' => 'Imprint', + 'privacy' => 'Privacy', + 'terms' => 'Terms', + 'guidelines' => 'Guidelines', + 'report' => 'Report content', + ], + + 'search' => [ + 'meta_title' => 'Search press releases – :brand', + 'meta_description' => 'Search the quality-checked press release archive by keyword, company and industry – permanently discoverable across the DACH region.', + 'eyebrow' => 'Archive search', + 'intro' => 'Keyword, company or industry – search the complete, quality-checked archive.', + 'placeholder' => 'Keyword, company or industry…', + 'searching' => 'searching…', + 'reset' => 'Reset search', + 'all_categories' => 'All sections', + 'results' => 'result|results', + 'for_term' => 'for ":term"', + 'sort' => 'Sort by', + 'sort_newest' => 'Newest first', + 'sort_oldest' => 'Oldest first', + 'no_results' => 'No results', + 'no_results_text' => 'There are currently no press releases matching your search. Try a different term or remove filters.', + 'pagination' => 'Pagination', + 'previous_page' => 'Previous page', + 'next_page' => 'Next page', + 'discover_title' => 'Search the archive', + 'discover_text' => ':count reviewed press releases — searchable by keyword, company and industry. Enter a search term above or choose a section.', + 'popular_categories' => 'Popular sections', + ], + + 'newsrooms_page' => [ + 'meta_title' => 'Newsrooms – :brand', + 'meta_description' => 'All active company newsrooms at a glance – quality-checked press releases per company, bundled and permanently discoverable across the DACH region.', + 'eyebrow' => 'Company newsrooms', + 'intro' => 'All active companies with their quality-checked press releases – bundled in one place.', + 'stat_total_sub' => 'with published releases', + 'stat_active' => 'Active (30 days)', + 'stat_archive' => 'Releases in the archive', + 'stat_archive_sub' => 'quality-checked', + 'search_label' => 'Search newsrooms', + 'search_placeholder' => 'Search companies…', + 'reset_filter' => 'Reset filter', + 'latest_release' => 'Latest release :date', + 'days_30' => '30 days', + 'none_found' => 'No newsrooms found', + 'none_found_term' => 'There is currently no newsroom matching ":term". Try a different search term.', + 'none_available' => 'There are currently no active newsrooms.', + 'cta_eyebrow' => 'Your own newsroom', + 'cta_title' => 'Bundle your press releases in your own newsroom.', + 'cta_text' => 'Your own branded URL · all releases in one place · SEO benefit for your company · quality-checked.', + ], + + 'newsroom_page' => [ + 'meta_description' => ':company newsroom: all quality-checked press releases bundled in one place.', + 'back_aria' => 'Back to all newsrooms', + 'all_newsrooms' => 'All newsrooms', + 'recent_stat' => ':count releases in the last 30 days', + 'since' => 'Newsroom since :year', + 'industries' => 'Industries', + 'last_30_days' => 'Last 30 days', + 'published_releases' => 'published releases', + 'total_hits' => 'Total views', + 'total_hits_sub' => 'across all releases', + 'latest' => 'Latest release', + 'empty' => 'No further releases in this newsroom at the moment.', + 'own_title' => 'Start your own newsroom', + 'own_text' => 'Bundle all your press releases under your own branded URL – with logo, industry mapping and SEO benefit.', + 'own_cta' => 'Create newsroom', + ], + + 'category' => [ + 'fallback_name' => 'Industry', + 'default_description' => 'Latest press releases from the :category sector – quality-checked and distributed across the DACH region.', + 'meta_title' => ':category press releases – :brand', + 'eyebrow' => 'Industry', + 'back_aria' => 'Back to all releases', + 'all_releases' => 'All releases', + 'month_stat' => ':count releases in the last 30 days · :today on :date', + 'sub_topics' => 'Subtopics', + 'stat_today' => 'Releases today', + 'vs_previous_week' => 'vs. previous week', + 'last_7_days' => 'in the last 7 days', + 'more_top' => 'More top stories', + 'no_more_top' => 'No further top stories at the moment.', + 'no_more_releases' => 'No further releases in this industry at the moment.', + 'week_stat' => ':count releases in the last 7 days', + 'search_archive' => 'Search the archive', + 'empty_title' => 'No releases in :category yet', + 'empty_text' => 'As soon as press releases are published in this industry, they will appear here.', + 'sub_industries' => 'Sub-industries', + 'volume_30_days' => 'volume last 30 days', + 'trend_7_days' => '7-day trend', + 'related' => 'Related industries', + 'cta_eyebrow' => 'For companies in :category', + 'cta_title' => 'Publish your press release directly in :category.', + 'cta_text' => 'Reach via active newsrooms · Structured distribution across the DACH region · Quality-checked.', + 'cta_submit' => 'Submit press release', + 'cta_pricing' => 'View plans & industry packages', + ], + + 'detail' => [ + 'breadcrumbs' => 'Breadcrumbs', + 'press_release' => 'Press release', + 'embargo_lifted' => 'Embargo lifted', + 'audience' => 'For business journalists', + 'time_at' => ':time', + 'words' => ':count words', + 'recommended' => 'Recommended', + 'recommended_hint' => 'This press release achieves a high content score — based on source quality, verification status and content relevance.', + 'image_placeholder' => 'Image · Press photo', + 'image_placeholder_company' => 'Image · Press photo :company', + 'photo' => 'Photo', + 'about_company' => 'About the company', + 'press_contact' => 'Press contact', + 'verified_hint' => 'Verified publisher — identity and domain confirmed', + 'verified_by' => 'Identity and domain confirmed by :brand', + 'verified_publisher' => 'Verified publisher', + 'published_by' => 'Published by', + 'to_newsroom' => 'Visit newsroom', + 'share' => 'Share', + 'email' => 'Email', + 'copy_link' => 'Copy link', + 'copied' => 'Copied!', + 'keywords' => 'Keywords', + 'further' => 'Further options', + 'print' => 'Print', + 'report_release' => 'Report press release', + 'privacy_rights' => 'Privacy & personal rights', + 'legal_question' => 'Do you have a legal concern about this release?', + 'report_content' => 'Report content', + 'remove_data' => 'Remove my data', + 'reports_free' => 'Reports are free of charge.', + 'more_from' => 'More from :company', + 'more_from_sub' => 'Latest releases from this newsroom', + 'all_from' => 'All releases from :company', + 'related' => 'Related releases', + 'related_sub' => 'Reactions, similar topics, industry context', + 'all_from_category' => 'All releases in :category', + ], + +]; diff --git a/resources/views/components/emails/layout.blade.php b/resources/views/components/emails/layout.blade.php index be9ee94e..a46a516a 100644 --- a/resources/views/components/emails/layout.blade.php +++ b/resources/views/components/emails/layout.blade.php @@ -86,7 +86,7 @@ presseecho und businessportal24 - — ein Konto, redaktionell geprüft, auf beiden Portalen veröffentlichen. + — ein Konto, qualitätsgeprüft, auf beiden Portalen veröffentlichen. diff --git a/resources/views/components/press-release-submit-modal.blade.php b/resources/views/components/press-release-submit-modal.blade.php index 2facd669..e25cf3d3 100644 --- a/resources/views/components/press-release-submit-modal.blade.php +++ b/resources/views/components/press-release-submit-modal.blade.php @@ -78,7 +78,7 @@
  • {{ __('Aussagen entsprechen Ihrem Wissensstand und sind sachlich richtig.') }}
  • - {{ __('Sie stellen die Plattform von Ansprüchen Dritter frei, die aus einer unberechtigten Nutzung von Inhalten resultieren. Die endgültige Veröffentlichung erfolgt nach redaktioneller Prüfung.') }} + {{ __('Sie stellen die Plattform von Ansprüchen Dritter frei, die aus einer unberechtigten Nutzung von Inhalten resultieren. Die endgültige Veröffentlichung erfolgt nach Abschluss der Inhaltsprüfung.') }}

    diff --git a/resources/views/components/web/active-newsrooms.blade.php b/resources/views/components/web/active-newsrooms.blade.php index 1dbd8c6c..7a4b316b 100644 --- a/resources/views/components/web/active-newsrooms.blade.php +++ b/resources/views/components/web/active-newsrooms.blade.php @@ -6,23 +6,13 @@ $palette = ['#0098A6', '#21A038', '#0018A8', '#1E1E1E', '#E20074', '#003781', '#C84A1E', '#5A6E2F']; $companies = collect($companies)->take(6)->values(); - - if ($companies->isEmpty()) { - $companies = collect([ - ['initial' => 'S', 'name' => 'Siemens AG', 'count' => 18, 'color' => '#0098A6'], - ['initial' => 'B', 'name' => 'BASF SE', 'count' => 14, 'color' => '#21A038'], - ['initial' => 'D', 'name' => 'Deutsche Bank', 'count' => 11, 'color' => '#0018A8'], - ['initial' => 'V', 'name' => 'Volkswagen AG', 'count' => 16, 'color' => '#1E1E1E'], - ['initial' => 'T', 'name' => 'Deutsche Telekom', 'count' => 9, 'color' => '#E20074'], - ['initial' => 'A', 'name' => 'Allianz SE', 'count' => 8, 'color' => '#003781'], - ]); - } @endphp +@if ($companies->isNotEmpty())
    -

    Aktive Newsrooms

    - Heute +

    {{ __('web.newsrooms.title') }}

    + {{ __('web.newsrooms.today') }}

    @@ -46,17 +36,22 @@
    {{ $name }}
    - {{ $count }} Meldungen letzte 7 Tage + {{ $count }} {{ __('web.newsrooms.count_suffix') }}
    - - - heute aktiv - + @if ($company['today'] ?? false) + + + {{ __('web.newsrooms.active_today') }} + + @else + + @endif @endforeach - Alle Newsrooms anzeigen → + {{ __('web.newsrooms.all') }} →
    +@endif diff --git a/resources/views/components/web/feed-ad.blade.php b/resources/views/components/web/feed-ad.blade.php deleted file mode 100644 index 7d6e10d9..00000000 --- a/resources/views/components/web/feed-ad.blade.php +++ /dev/null @@ -1,52 +0,0 @@ -@props(['ad']) - -@php - $time = $ad['time'] ?? ''; - $date = $ad['date'] ?? ''; - $category = $ad['category'] ?? 'Anzeige'; - $title = $ad['title'] ?? ''; - $company = $ad['company'] ?? ''; - $href = $ad['href'] ?? '#'; - - $catClass = 'self-start whitespace-nowrap text-[9.5px] font-bold uppercase leading-snug tracking-[0.14em] text-card-warm-cat max-md:mt-1 max-md:text-[9px] max-md:tracking-[0.1em]'; -@endphp - - diff --git a/resources/views/components/web/feed-item.blade.php b/resources/views/components/web/feed-item.blade.php index 9f7a9c64..062b61bc 100644 --- a/resources/views/components/web/feed-item.blade.php +++ b/resources/views/components/web/feed-item.blade.php @@ -1,49 +1,26 @@ @props([ 'release' => null, - 'mock' => null, 'recommended' => false, ]) @php - \Carbon\Carbon::setLocale('de'); + $category = $release?->category?->translations?->firstWhere('locale', app()->getLocale()) + ?? $release?->category?->translations?->first(); + $categoryName = $category?->name; + $company = $release?->company?->name; + $city = $release?->company?->city ?? null; + $title = $release->title; + $href = route('release.detail', ['slug' => $release->slug]); - if ($release) { - $category = $release?->category?->translations?->firstWhere('locale', 'de') - ?? $release?->category?->translations?->first(); - $categoryName = $category?->name ?? 'Wirtschaft'; - $company = $release?->company?->name ?? 'Unternehmen'; - $city = $release?->company?->city ?? null; - $title = $release->title; - $href = route('release.detail', ['slug' => $release->slug]); - $recommended = $recommended; - - $published = $release->published_at; - if ($published) { - $timeLine = $published->format('H:i'); - if ($published->isToday()) { - $dateLine = $published->translatedFormat('j. M'); - } elseif ($published->isYesterday()) { - $dateLine = 'Gestern'; - } else { - $dateLine = $published->translatedFormat('j. M'); - } - $datetimeAttr = $published->toIso8601String(); - } else { - $timeLine = '—'; - $dateLine = ''; - $datetimeAttr = null; - } + $published = $release->published_at; + if ($published) { + $timeLine = $published->format('H:i'); + $dateLine = $published->isYesterday() ? __('web.shared.yesterday') : $published->translatedFormat('j. M'); + $datetimeAttr = $published->toIso8601String(); } else { - $mock ??= []; - $categoryName = $mock['category'] ?? 'Wirtschaft'; - $company = $mock['company'] ?? ''; - $city = $mock['city'] ?? null; - $title = $mock['title'] ?? ''; - $href = $mock['href'] ?? '#'; - $timeLine = $mock['time'] ?? ''; - $dateLine = $mock['date'] ?? ''; + $timeLine = '—'; + $dateLine = ''; $datetimeAttr = null; - $recommended = $recommended || ($mock['recommended'] ?? false); } $bpCatClass = 'bp-cat self-start whitespace-nowrap leading-snug max-md:mt-1 max-md:text-[9px] max-md:tracking-[0.1em]'; @@ -80,7 +57,7 @@ - Empfehlung + {{ __('web.shared.recommendation') }} @endif diff --git a/resources/views/components/web/feed-top-item.blade.php b/resources/views/components/web/feed-top-item.blade.php index dfc7a552..0ddfc888 100644 --- a/resources/views/components/web/feed-top-item.blade.php +++ b/resources/views/components/web/feed-top-item.blade.php @@ -1,38 +1,22 @@ @props([ 'release' => null, - 'mock' => null, ]) @php - \Carbon\Carbon::setLocale('de'); - - if ($release) { - $category = $release?->category?->translations?->firstWhere('locale', 'de') - ?? $release?->category?->translations?->first(); - $categoryName = $category?->name ?? 'Wirtschaft'; - $image = $release?->images?->first(); - $imageUrl = $image?->variantUrl('card') ?? $image?->url(); - $teaser = (string) \Illuminate\Support\Str::of(strip_tags((string) ($release?->text ?? '')))->squish()->limit(180); - $company = $release?->company?->name ?? 'Unternehmen'; - $title = $release->title; - $href = route('release.detail', ['slug' => $release->slug]); - $time = $release?->published_at?->isToday() - ? $release->published_at->format('H:i') - : ($release?->published_at?->isYesterday() ? 'Gestern' : $release?->published_at?->isoFormat('D. MMM')); - $city = $release?->company?->city ?? null; - $readTime = max(1, (int) round(str_word_count(strip_tags((string) ($release?->text ?? ''))) / 200)); - } else { - $mock ??= []; - $categoryName = $mock['category'] ?? 'Bauen · Immobilien'; - $imageUrl = $mock['image'] ?? null; - $teaser = $mock['teaser'] ?? 'Nach 18 Monaten der Konsolidierung deuten Frühindikatoren auf eine Erholung hin. ImmoConsult-Studie analysiert 240 Märkte in DACH.'; - $company = $mock['company'] ?? 'ImmoConsult Deutschland'; - $title = $mock['title'] ?? 'Immobilienmarkt 2025: Experten prognostizieren Trendwende bei Kaufpreisen'; - $href = $mock['href'] ?? '#'; - $time = $mock['time'] ?? '14:18'; - $city = $mock['city'] ?? 'München'; - $readTime = $mock['readTime'] ?? 5; - } + $category = $release?->category?->translations?->firstWhere('locale', app()->getLocale()) + ?? $release?->category?->translations?->first(); + $categoryName = $category?->name; + $image = $release?->images?->first(); + $imageUrl = $image?->variantUrl('card') ?? $image?->url(); + $teaser = (string) \Illuminate\Support\Str::of(strip_tags((string) ($release?->text ?? '')))->squish()->limit(180); + $company = $release?->company?->name; + $title = $release->title; + $href = route('release.detail', ['slug' => $release->slug]); + $time = $release?->published_at?->isToday() + ? $release->published_at->format('H:i') + : ($release?->published_at?->isYesterday() ? __('web.shared.yesterday') : $release?->published_at?->isoFormat('D. MMM')); + $city = $release?->company?->city ?? null; + $readTime = max(1, (int) round(str_word_count(strip_tags((string) ($release?->text ?? ''))) / 200)); @endphp
    @@ -59,7 +43,7 @@ @endif - Top-Meldung + {{ __('web.hero.top_story') }}
    @@ -86,7 +70,7 @@ {{ $city }} @endif - {{ $readTime }} Min. Lesezeit + {{ __('web.shared.read_time', ['minutes' => $readTime]) }}
    diff --git a/resources/views/components/web/focus-ad.blade.php b/resources/views/components/web/focus-ad.blade.php deleted file mode 100644 index aa681e83..00000000 --- a/resources/views/components/web/focus-ad.blade.php +++ /dev/null @@ -1,50 +0,0 @@ -@props(['ad' => null]) - -@php - $ad ??= [ - 'category' => 'Logistik & Transport', - 'title' => 'DHL Express erweitert Same-Day-Netz auf 24 deutsche Großstädte', - 'company' => 'Deutsche Post DHL Group', - 'gradient' => 'linear-gradient(135deg,#3A2818,#A88466)', - 'href' => '#', - ]; - - $category = $ad['category'] ?? 'Anzeige'; - $title = $ad['title'] ?? 'Werbeplatz'; - $company = $ad['company'] ?? ''; - $gradient = $ad['gradient'] ?? 'linear-gradient(135deg,#3A2818,#A88466)'; - $href = $ad['href'] ?? '#'; -@endphp - - diff --git a/resources/views/components/web/focus-hero.blade.php b/resources/views/components/web/focus-hero.blade.php index 079b6421..184ae055 100644 --- a/resources/views/components/web/focus-hero.blade.php +++ b/resources/views/components/web/focus-hero.blade.php @@ -1,36 +1,33 @@ @props([ 'leadRelease' => null, 'sideReleases' => [], - 'advertisement' => null, ]) @php - \Carbon\Carbon::setLocale('de'); - - $leadCategory = $leadRelease?->category?->translations?->firstWhere('locale', 'de') - ?? $leadRelease?->category?->translations?->first(); - $leadCategoryName = $leadCategory?->name ?? 'KI & Innovation'; - $leadImage = $leadRelease?->images?->first(); - $leadImageUrl = $leadImage?->variantUrl('card') ?? $leadImage?->url(); - $leadTitle = $leadRelease?->title ?? 'Die Zukunft der KI im deutschen Mittelstand'; - $leadTeaser = $leadRelease - ? (string) \Illuminate\Support\Str::of(strip_tags((string) $leadRelease->text))->squish()->limit(200) - : 'Im exklusiven Interview spricht Dr. Maria Schmidt, Leiterin des KI-Instituts München, über Chancen und Herausforderungen der KI für mittelständische Unternehmen.'; - $leadCompany = $leadRelease?->company?->name ?? 'KI-Institut München'; - $leadHref = $leadRelease ? route('release.detail', ['slug' => $leadRelease->slug]) : '#'; - $leadReadTime = max(1, (int) round(str_word_count(strip_tags((string) ($leadRelease?->text ?? ''))) / 200)) ?: 5; - $leadPublishedAt = $leadRelease?->published_at; - $sideReleases = collect($sideReleases)->take(4)->values(); + + if ($leadRelease) { + $leadCategory = $leadRelease->category?->translations?->firstWhere('locale', app()->getLocale()) + ?? $leadRelease->category?->translations?->first(); + $leadCategoryName = $leadCategory?->name; + $leadImage = $leadRelease->images?->first(); + $leadImageUrl = $leadImage?->variantUrl('card') ?? $leadImage?->url(); + $leadTitle = $leadRelease->title; + $leadTeaser = (string) \Illuminate\Support\Str::of(strip_tags((string) $leadRelease->text))->squish()->limit(200); + $leadCompany = $leadRelease->company?->name; + $leadHref = route('release.detail', ['slug' => $leadRelease->slug]); + $leadReadTime = max(1, (int) round(str_word_count(strip_tags((string) $leadRelease->text)) / 200)); + $leadPublishedAt = $leadRelease->published_at; + } @endphp +@if ($leadRelease)

    - Im Fokus - · 🇩🇪 Deutschland + {{ __('web.hero.title') }}

    -
    {{ ucfirst(now()->isoFormat('dddd, HH:mm')) }} Uhr · Auswahl der Redaktion
    +
    {{ ucfirst(now()->isoFormat(__('web.hero.clock_format'))) }} · {{ __('web.hero.editorial_pick') }}

    @@ -46,29 +43,19 @@ loading="eager">
    @else - + @endif
    - TOP-MELDUNG - - Audio · {{ $leadReadTime * 4 }} min - + {{ __('web.hero.top_story') }}
    -
    {{ $leadCategoryName }}
    + @if ($leadCategoryName) +
    {{ $leadCategoryName }}
    + @endif

    {{ $leadTitle }}

    @@ -76,54 +63,31 @@ {{ $leadTeaser }}

    - {{ $leadCompany }} - + @if ($leadCompany) + {{ $leadCompany }} + + @endif @if ($leadPublishedAt) @endif - {{ $leadReadTime }} Min. Lesezeit + {{ __('web.shared.read_time', ['minutes' => $leadReadTime]) }}
    - -
    -
    - - - - - -
    -
    - 1 von 3 Top-Meldungen -
    -
    {{-- SIDEBAR: Was sonst zählt --}}
    +@endif diff --git a/resources/views/components/web/focus-side-item.blade.php b/resources/views/components/web/focus-side-item.blade.php index 1e242541..8f05545f 100644 --- a/resources/views/components/web/focus-side-item.blade.php +++ b/resources/views/components/web/focus-side-item.blade.php @@ -1,35 +1,23 @@ @props([ 'release' => null, - 'mock' => null, 'index' => 1, 'first' => false, ]) @php - if ($release) { - $category = $release?->category?->translations?->firstWhere('locale', 'de') - ?? $release?->category?->translations?->first(); - $categoryName = $category?->name ?? 'Wirtschaft'; - $time = $release?->published_at?->isToday() - ? $release->published_at->format('H:i') - : ($release?->published_at?->isYesterday() ? 'Gestern' : $release?->published_at?->isoFormat('D. MMM')); - $title = $release->title; - $company = $release?->company?->name ?? 'Unternehmen'; - $href = route('release.detail', ['slug' => $release->slug]); - $image = $release?->images?->first(); - $imageUrl = $image?->variantUrl('card') ?? $image?->url(); - $gradient = 'linear-gradient(135deg,#3A4A5A,#6B7C95)'; - $number = str_pad((string) $index, 2, '0', STR_PAD_LEFT); - } else { - $categoryName = $mock['cat']; - $time = $mock['time']; - $title = $mock['title']; - $company = $mock['company']; - $href = '#'; - $imageUrl = null; - $gradient = $mock['gradient'] ?? 'linear-gradient(135deg,#3A4A5A,#6B7C95)'; - $number = $mock['n']; - } + $category = $release?->category?->translations?->firstWhere('locale', app()->getLocale()) + ?? $release?->category?->translations?->first(); + $categoryName = $category?->name; + $time = $release?->published_at?->isToday() + ? $release->published_at->format('H:i') + : ($release?->published_at?->isYesterday() ? __('web.shared.yesterday') : $release?->published_at?->isoFormat('D. MMM')); + $title = $release->title; + $company = $release?->company?->name; + $href = route('release.detail', ['slug' => $release->slug]); + $image = $release?->images?->first(); + $imageUrl = $image?->variantUrl('card') ?? $image?->url(); + $gradient = 'linear-gradient(135deg,#3A4A5A,#6B7C95)'; + $number = str_pad((string) $index, 2, '0', STR_PAD_LEFT); @endphp
    diff --git a/resources/views/components/web/hero-fallback.blade.php b/resources/views/components/web/hero-fallback.blade.php new file mode 100644 index 00000000..8c7cc7ca --- /dev/null +++ b/resources/views/components/web/hero-fallback.blade.php @@ -0,0 +1,20 @@ +{{-- + Generierter Hintergrund für Top-Meldungen ohne Bild (Punktraster + Kreise), + identisch zur Startseiten-Hero. Liegt absolut im übergeordneten Container + (position: relative + bg-hero-grad erwartet). +--}} +@php + // Eindeutige Pattern-ID, falls die Komponente mehrfach pro Seite rendert. + $patternId = 'bp-hero-dots-'.\Illuminate\Support\Str::random(6); +@endphp + + diff --git a/resources/views/components/web/highlight-card.blade.php b/resources/views/components/web/highlight-card.blade.php deleted file mode 100644 index 406bbffa..00000000 --- a/resources/views/components/web/highlight-card.blade.php +++ /dev/null @@ -1,88 +0,0 @@ -@props(['highlight']) - -@php - // Map industry to Heroicon - $industryIcons = [ - 'IT & Digitalisierung' => 'cpu-chip', - 'KI & Innovation' => 'cpu-chip', - 'Industrie & Technik' => 'cog-6-tooth', - 'Finanzen & Versicherungen' => 'currency-dollar', - 'Handel & E-Commerce' => 'shopping-cart', - 'Bauen & Immobilien' => 'building-office', - 'Mobilität & Logistik' => 'truck', - 'Energie & Umwelt' => 'bolt', - 'Medizin & Gesundheit' => 'heart', - 'Personal & HR' => 'user-group', - 'Marketing, PR & Medien' => 'megaphone', - 'Recht & Steuern' => 'scale', - 'Politik, Verbände & NGOs' => 'flag', - 'Wissenschaft & Forschung' => 'beaker', - 'Lifestyle' => 'sparkles', - 'Tourismus & Kultur' => 'globe-alt', - ]; - - $iconName = $industryIcons[$highlight['industry']] ?? 'building-office'; - $iconPath = "/heroicons/optimized/24/outline/{$iconName}.svg"; -@endphp - - -
    -
    - -
    - {{ $highlight['title'] }} -
    - - {{ $highlight['badge'] }} - -
    -
    - - -
    -
    - {{-- Meta Info --}} -
    -
    -
    - {{ $highlight['industry'] }} -
    - {{ $highlight['industry'] }} -
    - -
    - - {{-- Title --}} -

    - {{ $highlight['title'] }} -

    - - {{-- Teaser --}} -

    - {{ $highlight['text'] }} -

    -
    - - {{-- Footer --}} - -
    -
    -
    -
    diff --git a/resources/views/components/web/industry-index.blade.php b/resources/views/components/web/industry-index.blade.php index 937fb5d5..f8f4e4fe 100644 --- a/resources/views/components/web/industry-index.blade.php +++ b/resources/views/components/web/industry-index.blade.php @@ -16,10 +16,11 @@ $industries = array_slice($industries, 0, 8); @endphp +@if (! empty($industries))
    -

    Branchen-Index

    - Letzte 7 Tage +

    {{ __('web.industry_index.title') }}

    + {{ __('web.industry_index.window') }}

    @@ -49,7 +50,7 @@
    {{ $industry['count'] }} - Meldungen + {{ __('web.industry_index.releases') }}
    @@ -58,3 +59,4 @@ @endforeach
    +@endif diff --git a/resources/views/components/web/industry-spotlight.blade.php b/resources/views/components/web/industry-spotlight.blade.php index 1dac0e99..8d3fe6b1 100644 --- a/resources/views/components/web/industry-spotlight.blade.php +++ b/resources/views/components/web/industry-spotlight.blade.php @@ -2,118 +2,73 @@ 'industry' => null, 'stats' => null, 'releases' => [], - 'study' => null, ]) @php - $industry ??= 'Energie & Klima'; - $stats ??= [ - ['label' => 'Meldungen heute', 'value' => '47', 'sub' => '+18 ggü. Wochenschnitt'], - ['label' => 'Investitions­volumen', 'value' => '€8,4 Mrd.', 'sub' => 'Q4 2025 angekündigt'], - ['label' => 'Aktive Unternehmen', 'value' => '23', 'sub' => 'in dieser Branche heute'], - ]; - $releases = collect($releases); - if ($releases->isEmpty()) { - $releases = collect([ - ['time' => '14:12', 'date' => '12. Mai', 'category' => 'Energie', 'title' => 'RWE startet 1,8 GW Offshore-Windpark vor Helgoland — größtes Projekt in der Nordsee 2025', 'company' => 'RWE AG', 'city' => 'Essen', 'minutes' => 4, 'href' => '#'], - ['time' => '12:28', 'date' => '12. Mai', 'category' => 'Energie', 'title' => 'EnBW unterzeichnet Wasserstoff-Lieferabkommen mit Air Liquide', 'company' => 'EnBW Energie', 'city' => 'Karlsruhe', 'minutes' => 3, 'href' => '#'], - ['time' => '10:55', 'date' => '12. Mai', 'category' => 'Klima', 'title' => 'Stadtwerke München erreichen 100 % Ökostrom-Ziel für Privatkunden', 'company' => 'SWM', 'city' => 'München', 'minutes' => 2, 'href' => '#'], - ]); - } - - $study ??= [ - 'kicker' => 'STUDIE · ENERGIE', - 'title' => 'Energiewende-Monitor 2025: Investitionen, Engpässe, Potenziale im DACH-Raum', - 'source' => 'Fraunhofer-Institut für Solare Energiesysteme', - 'meta' => 'PDF · 84 Seiten · Veröffentlicht 11. Nov 2025', - 'href' => '#', - ]; + $stats = collect($stats ?? []); @endphp +@if ($industry && $releases->isNotEmpty())

    - Heute im Fokus · {{ $industry }} + {{ __('web.spotlight.title') }} · {{ $industry }}

    -
    Aktualisiert {{ now()->format('H:i') }} Uhr · {{ $stats[0]['value'] ?? '47' }} Meldungen

    -
    - @foreach ($stats as $idx => $stat) -
    -
    {{ $stat['label'] }}
    -
    {{ $stat['value'] }}
    -
    {{ $stat['sub'] }}
    -
    - @endforeach -
    - -
    -
    - @foreach ($releases as $release) - -
    - {{ $release['time'] }} - @if (($release['date'] ?? '') !== '') - {{ $release['date'] }} - @endif - {{ $release['category'] }} -
    - -
    -
    - {{ $release['title'] }} -
    -
    - - {{ $release['company'] }} - @if ($release['city'] ?? null) - - {{ $release['city'] }} - @endif - @if ($release['minutes'] ?? null) - - {{ $release['minutes'] }} Min. - @endif -
    -
    -
    + @if ($stats->isNotEmpty()) +
    + @foreach ($stats as $idx => $stat) +
    +
    {{ $stat['label'] }}
    +
    {{ $stat['value'] }}
    +
    {{ $stat['sub'] }}
    +
    @endforeach
    + @endif - + + @endforeach
    +@endif diff --git a/resources/views/components/web/live-ticker.blade.php b/resources/views/components/web/live-ticker.blade.php index 41a233e2..d05f15bd 100644 --- a/resources/views/components/web/live-ticker.blade.php +++ b/resources/views/components/web/live-ticker.blade.php @@ -1,112 +1,93 @@ -@props([ - 'items' => [], - 'marketIndex' => null, - 'asideSlides' => null, -]) - @php - $items = collect($items)->all(); - if (empty($items)) { - $items = [ - ['time' => '14:32', 'text' => 'Siemens Energy hebt Jahresprognose an'], - ['time' => '14:18', 'text' => 'BASF: Restrukturierung in Ludwigshafen abgeschlossen'], - ['time' => '13:55', 'text' => 'Deutsche Bahn meldet Rekord bei Fernverkehr'], - ]; - } + // Selbstversorgend, damit der Ticker auf jeder Unterseite gefüllt ist + // (nicht nur auf der Startseite): jüngste PMs des Portals, kurz gecacht. + $items = app(\App\Services\Web\TickerData::class) + ->items(\App\Support\WebPortalContext::portalFromHost(), app()->getLocale()); - $marketIndex ??= [ - 'label' => 'DAX', - 'value' => '18.247', - 'delta' => '+0,8%', - 'positive' => true, - ]; - - $asideSlides = collect($asideSlides ?? [ - $marketIndex, - [ - 'label' => 'EUR/USD', - 'value' => '1,08', - 'delta' => '+0,2%', - 'positive' => true, - ], - [ - 'label' => 'Brent Öl', - 'value' => '78,40 $', - 'delta' => '-0,9%', - 'positive' => false, - ], - ])->values()->all(); + // Echte Kursdaten aus dem Cache (market:refresh); leer = Slot ausblenden. + $marketSlides = app(\App\Services\Web\MarketDataService::class)->slides(); @endphp -
    +@if (! empty($items) || ! empty($marketSlides)) +
    -
    - - - AD-HOC - + @if (! empty($items)) +
    + + + AD-HOC + - - Ad-hoc-Meldungen: - @foreach ($items as $item) - {{ $item['time'] }} {{ $item['text'] }}. - @endforeach - - - + @else + + @endif - + @if (! empty($marketSlides)) + - {{-- Kein position:absolute: sonst null Breite im Fluss, bis Layout kollabiert --}} -
    -
    - + {{-- Kein position:absolute: sonst null Breite im Fluss, bis Layout kollabiert --}} +
    +
    + +
    -
    + @endif
    +@endif diff --git a/resources/views/components/web/most-read.blade.php b/resources/views/components/web/most-read.blade.php index 501a0fe3..d4d6880a 100644 --- a/resources/views/components/web/most-read.blade.php +++ b/resources/views/components/web/most-read.blade.php @@ -3,29 +3,19 @@ ]) @php - $releases = collect($releases)->take(4)->values(); - - $fallback = collect([ - ['title' => 'FinTech-Startup PaymentFlow sichert sich 45 Mio. Euro', 'hits' => 12428], - ['title' => 'Energiewende: Neue Rekorde bei Erneuerbaren', 'hits' => 9831], - ['title' => 'Immobilienmarkt 2025: Trendwende bei Kaufpreisen', 'hits' => 7104], - ['title' => 'Telemedizin-Boom: 3 Mio. Online-Sprechstunden', 'hits' => 5298], + $items = collect($releases)->take(4)->values()->map(fn ($release) => [ + 'title' => \Illuminate\Support\Str::limit($release->title, 70), + 'hits' => (int) ($release->hits ?? 0), + 'href' => route('release.detail', ['slug' => $release->slug]), ]); - $items = $releases->isNotEmpty() - ? $releases->map(fn ($release) => [ - 'title' => \Illuminate\Support\Str::limit($release->title, 70), - 'hits' => (int) ($release->hits ?? 0), - 'href' => route('release.detail', ['slug' => $release->slug]), - ]) - : $fallback->map(fn ($mock) => array_merge($mock, ['href' => '#'])); - $maxHits = max(1, $items->max('hits')); @endphp +@if ($items->isNotEmpty())
    -

    Meistgelesen

    +

    {{ __('web.most_read.title') }}


    @@ -46,3 +36,4 @@ @endforeach
    +@endif diff --git a/resources/views/components/web/newsletter-strip.blade.php b/resources/views/components/web/newsletter-strip.blade.php deleted file mode 100644 index 5fafc7e1..00000000 --- a/resources/views/components/web/newsletter-strip.blade.php +++ /dev/null @@ -1,76 +0,0 @@ -@props([ - 'topics' => null, - 'subscribers' => 84000, -]) - -@php - $themeKey = config('app.theme'); - $topics ??= config('domains.domains.'.$themeKey.'.brand.newsletter_topics', [ - ['name' => 'Tageszusammenfassung', 'meta' => 'Mo–Fr · 07:00 Uhr', 'active' => true], - ['name' => 'Wochenrückblick', 'meta' => 'Sonntags · 18:00 Uhr', 'active' => false], - ['name' => 'Energie & Klima', 'meta' => 'ab Content-Score 80 · max. 2× pro Woche', 'active' => true], - ['name' => 'IPO & M&A', 'meta' => 'ausgewählt von der Redaktion · ca. 3–5 Mails/Monat', 'active' => false], - ]); -@endphp - -
    -
    - - -
    -
    -
    Bleiben Sie informiert
    -

    - Pressemeldungen, kuratiert in Ihren Posteingang -

    -

    - Die wichtigsten Meldungen aus DACH — täglich, wöchentlich oder branchenspezifisch. Keine Werbung, jederzeit kündbar. -

    - -
    - @csrf - - - -
    -
    - Über {{ number_format($subscribers, 0, ',', '.') }} Abonnenten · Datenschutz nach DSGVO -
    -
    - -
    - @foreach ($topics as $topic) - - @endforeach -
    -
    -
    -
    diff --git a/resources/views/components/web/pagination.blade.php b/resources/views/components/web/pagination.blade.php index 7ef38d40..9211e603 100644 --- a/resources/views/components/web/pagination.blade.php +++ b/resources/views/components/web/pagination.blade.php @@ -7,7 +7,7 @@ $start = max(1, $paginator->currentPage() - 2); $end = min($paginator->lastPage(), $paginator->currentPage() + 2); @endphp -
    diff --git a/resources/views/components/web/site-header.blade.php b/resources/views/components/web/site-header.blade.php index 55949e45..009669a6 100644 --- a/resources/views/components/web/site-header.blade.php +++ b/resources/views/components/web/site-header.blade.php @@ -1,8 +1,5 @@ @props([ 'navigation' => null, - 'ticker' => [], - 'marketIndex' => null, - 'asideSlides' => null, 'brand' => null, ]) @@ -32,7 +29,7 @@ // Navigation = echte Rubriken der aktiven Ausgabe. Es gibt keine separate // Übersichtsseite; jeder Eintrag führt direkt auf die Kategorieseite. $navigation ??= collect([ - ['label' => 'Startseite', 'href' => route('web.home'), 'active' => request()->routeIs('web.home')], + ['label' => __('web.nav.home'), 'href' => route('web.home'), 'active' => request()->routeIs('web.home')], ])->concat( \App\Support\CategoryNavigation::items()->map(fn (array $category): array => [ 'label' => $category['label'], @@ -41,7 +38,7 @@ ]) )->all(); - $activeLabel = collect($navigation)->firstWhere('active', true)['label'] ?? 'Menü'; + $activeLabel = collect($navigation)->firstWhere('active', true)['label'] ?? __('web.nav.menu'); @endphp
    @@ -54,7 +51,7 @@ @foreach ($editions as $edition) @@ -62,7 +59,7 @@ class="flex items-center gap-1 sm:gap-1.5 px-1.5 sm:px-2.5 py-2 text-[11px] sm:text-[11.5px] whitespace-nowrap -mb-px border-b-2 transition-colors @if ($edition['code'] === $activeEdition) font-semibold text-ink-on-dark bg-white/[0.06] border-brand @else font-medium text-ink-on-dark-2 border-transparent hover:text-ink-on-dark hover:bg-white/[0.04] @endif" - aria-label="Ausgabe {{ $edition['label'] }}" + aria-label="{{ __('web.header.edition') }} {{ $edition['label'] }}" @if ($edition['code'] === $activeEdition) aria-current="true" @endif> {{ $edition['flag'] }} {{ $edition['label'] }} @@ -71,7 +68,7 @@ - {{-- Sprache: mobil verkürzt; ab lg mit Newsletter/RSS/API --}} + {{-- Sprache: mobil verkürzt; ab lg mit FAQ/API --}} - Preise + {{ __('web.header.pricing') }} @@ -95,9 +92,11 @@ {{-- Header (Logo + Suche + Buttons + Burger) --}}
    - +
    @@ -215,12 +214,12 @@ - Erweiterte Suche + {{ __('web.header.advanced_search') }}
    @@ -234,7 +233,7 @@ x-data="{ menuOpen: false }" @keydown.escape.window="menuOpen = false" @click.outside="menuOpen = false" - aria-label="Hauptnavigation"> + aria-label="{{ __('web.nav.aria_label') }}"> {{-- Desktop: horizontale Tabs --}}
    diff --git a/resources/views/livewire/admin/payments/plans.blade.php b/resources/views/livewire/admin/payments/plans.blade.php index 7796e039..7782676a 100644 --- a/resources/views/livewire/admin/payments/plans.blade.php +++ b/resources/views/livewire/admin/payments/plans.blade.php @@ -280,7 +280,7 @@ new #[Layout('components.layouts.app'), Title('Tarife & Pakete')] class extends
    diff --git a/resources/views/livewire/admin/press-releases/create.blade.php b/resources/views/livewire/admin/press-releases/create.blade.php index 48a6b4ef..7338fe2d 100644 --- a/resources/views/livewire/admin/press-releases/create.blade.php +++ b/resources/views/livewire/admin/press-releases/create.blade.php @@ -392,7 +392,7 @@ new #[Layout('components.layouts.app'), Title('Neue Pressemitteilung')] class ex @if ($selectedCompany->website)

    {{ __('Web') }}: + class="font-semibold text-[color:var(--color-ink-2)]">Web: {{ $selectedCompany->website }}

    @endif diff --git a/resources/views/livewire/admin/press-releases/edit.blade.php b/resources/views/livewire/admin/press-releases/edit.blade.php index 69cb3e44..33a8b059 100644 --- a/resources/views/livewire/admin/press-releases/edit.blade.php +++ b/resources/views/livewire/admin/press-releases/edit.blade.php @@ -655,7 +655,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung bearbeiten')] cl @if ($selectedCompany->website)

    {{ __('Web') }}: + class="font-semibold text-[color:var(--color-ink-2)]">Web: {{ $selectedCompany->website }}

    @endif diff --git a/resources/views/livewire/admin/press-releases/index.blade.php b/resources/views/livewire/admin/press-releases/index.blade.php index 6a943bc1..8e40a54e 100644 --- a/resources/views/livewire/admin/press-releases/index.blade.php +++ b/resources/views/livewire/admin/press-releases/index.blade.php @@ -404,7 +404,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilungen')] class exten {{ __('queue') }} - {{ __('redaktionelle Prüfung') }} + {{ __('Inhaltsprüfung') }} {{ __('privat') }} diff --git a/resources/views/livewire/admin/press-releases/show.blade.php b/resources/views/livewire/admin/press-releases/show.blade.php index b624c27c..35ca7ca5 100644 --- a/resources/views/livewire/admin/press-releases/show.blade.php +++ b/resources/views/livewire/admin/press-releases/show.blade.php @@ -253,7 +253,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
    -

    {{ __('Diese PM wartet auf eine redaktionelle Entscheidung.') }}

    +

    {{ __('Diese PM wartet auf eine Prüf-Entscheidung.') }}

    @if ($latestClassification && $latestClassification->reason)

    {{ __('KI-Hinweis') }}: diff --git a/resources/views/livewire/customer/dashboard.blade.php b/resources/views/livewire/customer/dashboard.blade.php index 0725c9eb..17d8fdf6 100644 --- a/resources/views/livewire/customer/dashboard.blade.php +++ b/resources/views/livewire/customer/dashboard.blade.php @@ -294,7 +294,7 @@ new #[Layout('components.layouts.app'), Title('Mein Dashboard')] class extends C {{ __('Ø 4 h') }} - {{ __('redaktionelle Prüfung') }} + {{ __('Inhaltsprüfung') }} @@ -365,7 +365,7 @@ new #[Layout('components.layouts.app'), Title('Mein Dashboard')] class extends C {{ __('Noch keine Pressemitteilungen') }}

    - {{ __('Starten Sie mit einer ersten Mitteilung für die aktive Firma oder Ihr Kundenkonto. Veröffentlichung erfolgt nach redaktioneller Prüfung auf beiden Portalen.') }} + {{ __('Starten Sie mit einer ersten Mitteilung für die aktive Firma oder Ihr Kundenkonto. Veröffentlichung erfolgt nach bestandener Inhaltsprüfung auf beiden Portalen.') }}

    diff --git a/resources/views/livewire/customer/press-kits/create.blade.php b/resources/views/livewire/customer/press-kits/create.blade.php index 8123ef2c..89620cff 100644 --- a/resources/views/livewire/customer/press-kits/create.blade.php +++ b/resources/views/livewire/customer/press-kits/create.blade.php @@ -131,7 +131,7 @@ new #[Layout('components.layouts.app'), Title('Neue Firma anlegen')] class exten {{ __('Neue Firma anlegen') }}

    - {{ __('Lege Stammdaten und Portal-Zuordnung an. Die Firma steht sofort zur Verfügung — die redaktionelle Prüfung erfolgt erst bei der ersten Pressemitteilung.') }} + {{ __('Lege Stammdaten und Portal-Zuordnung an. Die Firma steht sofort zur Verfügung — die Inhaltsprüfung erfolgt erst bei der ersten Pressemitteilung.') }}

    diff --git a/resources/views/livewire/customer/press-kits/index.blade.php b/resources/views/livewire/customer/press-kits/index.blade.php index 607f09cc..98b4e23b 100644 --- a/resources/views/livewire/customer/press-kits/index.blade.php +++ b/resources/views/livewire/customer/press-kits/index.blade.php @@ -430,7 +430,7 @@ new #[Layout('components.layouts.app'), Title('Meine Firmen')] class extends Com

    - {{ __('Eine Firma ist der Container für Pressemitteilungen: Stammdaten, Boilerplate, Pressekontakte. Anlage ohne separate Freigabe — die redaktionelle Prüfung erfolgt erst bei der Pressemitteilung.') }} + {{ __('Eine Firma ist der Container für Pressemitteilungen: Stammdaten, Boilerplate, Pressekontakte. Anlage ohne separate Freigabe — die Inhaltsprüfung erfolgt erst bei der Pressemitteilung.') }}

    diff --git a/resources/views/livewire/customer/press-releases/create.blade.php b/resources/views/livewire/customer/press-releases/create.blade.php index aedd49ba..978638c6 100644 --- a/resources/views/livewire/customer/press-releases/create.blade.php +++ b/resources/views/livewire/customer/press-releases/create.blade.php @@ -334,7 +334,7 @@ new #[Layout('components.layouts.app'), Title('Neue Pressemitteilung')] class ex Flux::toast( heading: __('Eingereicht'), - text: __('Die Redaktion prüft die Pressemitteilung typischerweise innerhalb von 24 h.'), + text: __('Die Inhaltsprüfung ist typischerweise innerhalb von 24 h abgeschlossen.'), variant: 'success', ); } else { @@ -734,7 +734,7 @@ new #[Layout('components.layouts.app'), Title('Neue Pressemitteilung')] class ex

    {!! nl2br(e($selectedCompany->boilerplate)) !!}

    @if ($selectedCompany->website)

    - {{ __('Web') }}: + Web: {{ $selectedCompany->website }}

    @endif @@ -789,7 +789,7 @@ new #[Layout('components.layouts.app'), Title('Neue Pressemitteilung')] class ex

    - {{ __('Warnungen blockieren nicht. Pflichtfelder blockieren. Die Redaktion prüft typ. innerhalb von 24h.') }} + {{ __('Warnungen blockieren nicht. Pflichtfelder blockieren. Die Inhaltsprüfung dauert typ. bis zu 24h.') }}


    @@ -979,7 +979,7 @@ new #[Layout('components.layouts.app'), Title('Neue Pressemitteilung')] class ex {{ __('Sofort nach Freigabe') }} - {{ __('geht live, sobald die Redaktion grünes Licht gibt') }} + {{ __('geht live, sobald die Inhaltsprüfung grünes Licht gibt') }} diff --git a/resources/views/livewire/customer/press-releases/edit.blade.php b/resources/views/livewire/customer/press-releases/edit.blade.php index 21997eec..efb58e8a 100644 --- a/resources/views/livewire/customer/press-releases/edit.blade.php +++ b/resources/views/livewire/customer/press-releases/edit.blade.php @@ -324,7 +324,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung bearbeiten')] cl Flux::toast( heading: __('Eingereicht'), - text: __('Die Redaktion prüft die Pressemitteilung typischerweise innerhalb von 24 h.'), + text: __('Die Inhaltsprüfung ist typischerweise innerhalb von 24 h abgeschlossen.'), variant: 'success', ); } else { @@ -656,7 +656,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung bearbeiten')] cl

    {!! nl2br(e($selectedCompany->boilerplate)) !!}

    @if ($selectedCompany->website)

    - {{ __('Web') }}: + Web: {{ $selectedCompany->website }}

    @endif @@ -715,7 +715,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung bearbeiten')] cl

    - {{ __('Warnungen blockieren nicht. Pflichtfelder blockieren. Die Redaktion prüft typ. innerhalb von 24h.') }} + {{ __('Warnungen blockieren nicht. Pflichtfelder blockieren. Die Inhaltsprüfung dauert typ. bis zu 24h.') }}


    @@ -941,7 +941,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung bearbeiten')] cl {{ __('Sofort nach Freigabe') }} - {{ __('geht live, sobald die Redaktion grünes Licht gibt') }} + {{ __('geht live, sobald die Inhaltsprüfung grünes Licht gibt') }} diff --git a/resources/views/livewire/customer/press-releases/index.blade.php b/resources/views/livewire/customer/press-releases/index.blade.php index 20221df1..e41ddff6 100644 --- a/resources/views/livewire/customer/press-releases/index.blade.php +++ b/resources/views/livewire/customer/press-releases/index.blade.php @@ -459,7 +459,7 @@ new #[Layout('components.layouts.app'), Title('Meine Pressemitteilungen')] class Schnell-Submits, der die Bestätigungen umgehen würde. --}} @@ -579,7 +579,7 @@ new #[Layout('components.layouts.app'), Title('Meine Pressemitteilungen')] class {{-- Empty: gar keine PMs überhaupt --}} @@ -653,7 +653,7 @@ new #[Layout('components.layouts.app'), Title('Meine Pressemitteilungen')] class
    • {{ __('Vorschau') }}
    • -
    • {{ __('Warten auf Redaktion') }}
    • +
    • {{ __('Warten auf Prüfung') }}
    diff --git a/resources/views/livewire/customer/press-releases/show.blade.php b/resources/views/livewire/customer/press-releases/show.blade.php index 0f7d9805..0ba1f82b 100644 --- a/resources/views/livewire/customer/press-releases/show.blade.php +++ b/resources/views/livewire/customer/press-releases/show.blade.php @@ -77,7 +77,7 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends Flux::toast( heading: __('Eingereicht'), - text: __('Die Redaktion prüft die Pressemitteilung typischerweise innerhalb von 24 h.'), + text: __('Die Inhaltsprüfung ist typischerweise innerhalb von 24 h abgeschlossen.'), variant: 'success', ); } diff --git a/resources/views/livewire/web/featured-releases.blade.php b/resources/views/livewire/web/featured-releases.blade.php deleted file mode 100644 index 88e33d4c..00000000 --- a/resources/views/livewire/web/featured-releases.blade.php +++ /dev/null @@ -1,101 +0,0 @@ - [ - [ - 'slug' => 'ki-revolution-deutsche-unternehmen', - 'title' => 'KI-Revolution: Deutsche Unternehmen investieren Milliarden in Automatisierung', - 'teaser' => 'Eine neue Studie zeigt: Unternehmen im DACH-Raum planen für 2025 Investitionen in Höhe von über 15 Milliarden Euro in KI-gestützte Automatisierungslösungen.', - 'company' => 'TechVision Analytics', - 'industry' => 'IT & Software', - 'region' => 'Deutschland', - 'date' => 'Heute, 14:30', - 'hasImage' => true, - 'hasPdf' => true, - 'imageUrl' => 'https://images.unsplash.com/photo-1677442136019-21780ecad995?w=800&h=600&fit=crop', - 'companyLogo' => 'logo', - ], - [ - 'slug' => 'energiewende-beschleunigt', - 'title' => 'Energiewende beschleunigt sich: Neue Rekorde bei erneuerbaren Energien', - 'teaser' => 'Im ersten Quartal 2025 erreicht der Anteil erneuerbarer Energien am Strommix einen historischen Höchststand von 58%. Experten sprechen von einem Wendepunkt.', - 'company' => 'GreenPower Deutschland', - 'industry' => 'Energie', - 'region' => 'Deutschland', - 'date' => 'Heute, 11:15', - 'hasImage' => true, - 'imageUrl' => 'https://images.unsplash.com/photo-1466611653911-95081537e5b7?w=800&h=600&fit=crop', - ], - [ - 'slug' => 'fintech-startup-series-b', - 'title' => 'FinTech-Startup sichert sich 45 Millionen Euro in Series-B-Runde', - 'teaser' => 'Das Berliner FinTech-Startup PaymentFlow konnte in einer Series-B-Finanzierungsrunde 45 Millionen Euro einsammeln.', - 'company' => 'PaymentFlow GmbH', - 'industry' => 'Finanzen', - 'region' => 'Berlin', - 'date' => 'Gestern, 16:45', - 'hasPdf' => true, - 'imageUrl' => 'https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=800&h=600&fit=crop', - ], - ] - ]; - } -}; ?> - -
    -
    - -
    - -
    - - -
    - - - -
    -
    -
    diff --git a/resources/views/livewire/web/press-release-feed.blade.php b/resources/views/livewire/web/press-release-feed.blade.php index adca8272..8c170998 100644 --- a/resources/views/livewire/web/press-release-feed.blade.php +++ b/resources/views/livewire/web/press-release-feed.blade.php @@ -94,20 +94,20 @@ new class extends Component

    - Aktuelle Meldungen + {{ __('web.feed.title') }}

    @@ -117,55 +117,23 @@ new class extends Component $items = $releases->items(); $top = $items[0] ?? null; $rest = array_slice($items, 1); - - $mockFeedItems = [ - ['time' => '13:42', 'date' => '12. Mai', 'category' => 'Tourismus', 'title' => 'Nachhaltiger Tourismus auf Erfolgskurs: Buchungen steigen um 45 %', 'company' => 'GreenTravel Consulting', 'city' => 'Berlin', 'recommended' => true], - ['time' => '12:55', 'date' => '12. Mai', 'category' => 'Bildung', 'title' => 'Digitalisierung im Bildungssektor: Schulen erhalten 2 Mrd. Euro Förderung', 'company' => 'EduTech Initiative', 'city' => 'Frankfurt'], - ['time' => '11:20', 'date' => '12. Mai', 'category' => 'Medien', 'title' => 'Medienbranche im Umbruch: Streaming-Dienste überholen klassisches TV', 'company' => 'MediaWatch Analytics', 'city' => 'Hamburg'], - ['time' => '10:48', 'date' => '12. Mai', 'category' => 'Handel', 'title' => 'Einzelhandel setzt auf KI: Personalisierte Shopping-Erlebnisse werden Standard', 'company' => 'RetailTech Innovations', 'city' => 'Köln', 'recommended' => true], - ['time' => '09:33', 'date' => '12. Mai', 'category' => 'Gesundheit', 'title' => 'Telemedizin-Boom: 3 Millionen Online-Sprechstunden im letzten Quartal', 'company' => 'HealthConnect Digital', 'city' => 'Stuttgart'], - ['time' => '08:15', 'date' => '12. Mai', 'category' => 'Mobilität', 'title' => 'E-Mobilität: Ladeinfrastruktur wächst um 38 % gegenüber Vorjahr', 'company' => 'eMobility Verband', 'city' => 'Düsseldorf'], - ]; - - $inFeedAd = [ - 'time' => '12:14', - 'date' => now()->translatedFormat('j. M'), - 'category' => 'Cloud · Software', - 'title' => 'Microsoft Azure: Neue EU-Region Frankfurt mit DSGVO-zertifizierter KI-Infrastruktur', - 'company' => 'Microsoft Deutschland GmbH', - ]; @endphp @if ($top) @else - +

    {{ __('web.feed.empty') }}

    @endif - @if (! empty($rest)) - @foreach ($rest as $i => $release) - - @endforeach - @else - @foreach ($mockFeedItems as $mock) - - @endforeach - @endif - - + @foreach ($rest as $release) + + @endforeach @if ($releases->hasMorePages()) - @elseif (! $top) - @endif diff --git a/resources/views/livewire/web/press-releases-grid.blade.php b/resources/views/livewire/web/press-releases-grid.blade.php deleted file mode 100644 index 49cbdd7a..00000000 --- a/resources/views/livewire/web/press-releases-grid.blade.php +++ /dev/null @@ -1,187 +0,0 @@ - [ - [ - 'slug' => 'ki-revolution-deutsche-unternehmen', - 'title' => 'KI-Revolution: Deutsche Unternehmen investieren Milliarden in Automatisierung', - 'teaser' => 'Eine neue Studie zeigt: Unternehmen im DACH-Raum planen für 2025 Investitionen in Höhe von über 15 Milliarden Euro in KI-gestützte Automatisierungslösungen.', - 'company' => 'TechVision Analytics', - 'industry' => 'IT & Digitalisierung', - 'region' => 'Deutschland', - 'date' => '17. Okt 2024', - 'contentType' => 'ANALYSE', - 'hasImage' => true, - 'hasPdf' => true, - 'imageUrl' => 'https://images.unsplash.com/photo-1677442136019-21780ecad995?w=800&h=600&fit=crop', - 'companyLogo' => 'logo', - ], - [ - 'slug' => 'energiewende-beschleunigt', - 'title' => 'Energiewende beschleunigt sich: Neue Rekorde bei erneuerbaren Energien', - 'teaser' => 'Im ersten Quartal 2025 erreicht der Anteil erneuerbarer Energien am Strommix einen historischen Höchststand von 58%.', - 'company' => 'GreenPower Deutschland', - 'industry' => 'Energie & Umwelt', - 'region' => 'Deutschland', - 'date' => '16. Okt 2024', - 'contentType' => 'FACHMELDUNG', - 'hasImage' => true, - 'imageUrl' => 'https://images.unsplash.com/photo-1466611653911-95081537e5b7?w=800&h=600&fit=crop', - ], - [ - 'slug' => 'fintech-startup-series-b', - 'title' => 'FinTech-Startup sichert sich 45 Millionen Euro in Series-B-Runde', - 'teaser' => 'Das Berliner FinTech-Startup PaymentFlow konnte in einer Series-B-Finanzierungsrunde 45 Millionen Euro einsammeln.', - 'company' => 'PaymentFlow GmbH', - 'industry' => 'Finanzen & Versicherungen', - 'region' => 'Berlin', - 'date' => '15. Okt 2024', - 'contentType' => 'FACHMELDUNG', - 'hasPdf' => true, - 'imageUrl' => null, - ], - [ - 'slug' => 'gesundheitsbranche-digital', - 'title' => 'Gesundheitsbranche setzt verstärkt auf digitale Lösungen', - 'teaser' => 'Telemedizin und KI-gestützte Diagnostik werden zum Standard: 78% der Krankenhäuser in Deutschland planen Investitionen.', - 'company' => 'MediTech Solutions', - 'industry' => 'Medizin & Gesundheit', - 'region' => 'München', - 'date' => '14. Okt 2024', - 'contentType' => 'INTERVIEW', - 'hasImage' => true, - 'imageUrl' => 'https://images.unsplash.com/photo-1576091160399-112ba8d25d1d?w=800&h=600&fit=crop', - ], - [ - 'slug' => 'automobilindustrie-transformation', - 'title' => 'Automobilindustrie: Transformation zur E-Mobilität nimmt Fahrt auf', - 'teaser' => 'Führende Automobilhersteller kündigen massive Investitionen in E-Mobilität an. Bis 2030 sollen 80% der Neufahrzeuge elektrisch sein.', - 'company' => 'Auto Industry Report', - 'industry' => 'Mobilität & Logistik', - 'region' => 'Stuttgart', - 'date' => '13. Okt 2024', - 'contentType' => 'ANALYSE', - 'hasImage' => true, - 'hasPdf' => true, - 'imageUrl' => 'https://images.unsplash.com/photo-1593941707882-a5bba14938c7?w=800&h=600&fit=crop', - ], - [ - 'slug' => 'cybersecurity-massnahmen', - 'title' => 'Cybersecurity: Unternehmen verstärken Schutzmaßnahmen gegen Hackerangriffe', - 'teaser' => 'Nach einer Serie von Cyberattacken erhöhen deutsche Unternehmen ihre Investitionen in IT-Sicherheit um durchschnittlich 35%.', - 'company' => 'CyberSafe Europe', - 'industry' => 'IT & Digitalisierung', - 'region' => 'Frankfurt', - 'date' => '12. Okt 2024', - 'contentType' => 'FACHMELDUNG', - 'hasImage' => true, - 'imageUrl' => 'https://images.unsplash.com/photo-1550751827-4bd374c3f58b?w=800&h=600&fit=crop', - ], - // Neue Artikel ohne Bild für verschiedene Branchen - [ - 'slug' => 'immobilienmarkt-trendwende', - 'title' => 'Immobilienmarkt 2025: Experten prognostizieren Trendwende bei Kaufpreisen', - 'teaser' => 'Nach Jahren steigender Preise zeigen aktuelle Analysen eine Stabilisierung des Immobilienmarktes. Besonders Metropolregionen verzeichnen erstmals rückläufige Tendenzen.', - 'company' => 'ImmoConsult Deutschland', - 'industry' => 'Bauen & Immobilien', - 'region' => 'Hamburg', - 'date' => '11. Okt 2024', - 'contentType' => 'ANALYSE', - 'hasImage' => false, - 'hasPdf' => true, - 'imageUrl' => null, - ], - [ - 'slug' => 'tourismus-nachhaltigkeit', - 'title' => 'Nachhaltiger Tourismus auf Erfolgskurs: Buchungen steigen um 45%', - 'teaser' => 'Öko-Hotels und klimaneutrales Reisen liegen im Trend. Die Reisebranche verzeichnet einen deutlichen Wandel im Buchungsverhalten deutscher Urlauber.', - 'company' => 'GreenTravel Consulting', - 'industry' => 'Tourismus & Kultur', - 'region' => 'Österreich', - 'date' => '10. Okt 2024', - 'contentType' => 'FACHMELDUNG', - 'hasImage' => false, - 'imageUrl' => null, - ], - [ - 'slug' => 'bildungssektor-digitalisierung', - 'title' => 'Digitalisierung im Bildungssektor: Schulen erhalten 2 Milliarden Euro Förderung', - 'teaser' => 'Bundesweites Digitalpakt-Programm wird ausgeweitet. Schwerpunkt liegt auf KI-gestützten Lernplattformen und moderner IT-Infrastruktur für Schulen.', - 'company' => 'EduTech Initiative', - 'industry' => 'Wissenschaft & Forschung', - 'region' => 'Berlin', - 'date' => '09. Okt 2024', - 'contentType' => 'FACHMELDUNG', - 'hasImage' => false, - 'hasPdf' => true, - 'imageUrl' => null, - ], - [ - 'slug' => 'medienbranche-streaming', - 'title' => 'Medienbranche im Umbruch: Streaming-Dienste überholen klassisches TV', - 'teaser' => 'Erstmals nutzen mehr Deutsche regelmäßig Streaming-Plattformen als lineares Fernsehen. Die Werbebudgets verschieben sich entsprechend.', - 'company' => 'MediaWatch Analytics', - 'industry' => 'Marketing, PR & Medien', - 'region' => 'Köln', - 'date' => '08. Okt 2024', - 'contentType' => 'ANALYSE', - 'hasImage' => false, - 'imageUrl' => null, - ], - [ - 'slug' => 'einzelhandel-ki-revolution', - 'title' => 'Einzelhandel setzt auf KI: Personalisierte Shopping-Erlebnisse werden Standard', - 'teaser' => 'Künstliche Intelligenz revolutioniert den Handel. Von virtuellen Anproben bis zu KI-gestützter Beratung – der Einzelhandel wird digital.', - 'company' => 'RetailTech Innovations', - 'industry' => 'Handel & E-Commerce', - 'region' => 'Düsseldorf', - 'date' => '07. Okt 2024', - 'contentType' => 'INTERVIEW', - 'hasImage' => false, - 'hasPdf' => true, - 'imageUrl' => null, - ], - [ - 'slug' => 'gesundheit-telemedizin-boom', - 'title' => 'Telemedizin-Boom: 3 Millionen Online-Sprechstunden im letzten Quartal', - 'teaser' => 'Die Akzeptanz von Videosprechstunden erreicht neue Höchststände. Besonders ländliche Regionen profitieren von der digitalen Gesundheitsversorgung.', - 'company' => 'HealthConnect Digital', - 'industry' => 'Medizin & Gesundheit', - 'region' => 'Schweiz', - 'date' => '06. Okt 2024', - 'contentType' => 'FACHMELDUNG', - 'hasImage' => false, - 'imageUrl' => null, - ], - ], - ]; - } -}; ?> - -
    - @php - $theme = config('app.theme', 'businessportal24'); - $isPresseecho = $theme === 'presseecho'; - @endphp - -
    - @foreach ($releases as $release) - @if ($isPresseecho) - {{-- Presseecho: Hochwertige Magazine-Card --}} - - @else - {{-- Businessportal24: Standard-Card --}} - - @endif - @endforeach -
    -
    diff --git a/resources/views/livewire/web/search.blade.php b/resources/views/livewire/web/search.blade.php index fc5ca4b4..c9c5e4bc 100644 --- a/resources/views/livewire/web/search.blade.php +++ b/resources/views/livewire/web/search.blade.php @@ -165,7 +165,7 @@ new class extends Component
    {{-- Suchfeld --}}
    - +
    - sucht… + {{ __('web.search.searching') }} @if ($q !== '' || $category !== '') @foreach ($categories as $cat)
    @@ -237,22 +237,22 @@ new class extends Component @empty
    -

    Keine Treffer

    +

    {{ __('web.search.no_results') }}

    - Für Ihre Suche gibt es derzeit keine Pressemitteilungen. Versuchen Sie einen anderen Begriff oder entfernen Sie Filter. + {{ __('web.search.no_results_text') }}

    @endforelse @if ($results->hasPages()) -
    -

    Durchsuchen Sie das Archiv

    +

    {{ __('web.search.discover_title') }}

    - {{ number_format($archiveTotal, 0, ',', '.') }} geprüfte Pressemitteilungen — durchsuchbar nach Stichwort, - Unternehmen und Branche. Geben Sie oben einen Suchbegriff ein oder wählen Sie eine Rubrik. + {{ __('web.search.discover_text', ['count' => number_format($archiveTotal, 0, ',', '.')]) }}

    @if ($categories->isNotEmpty())
    -
    Beliebte Rubriken
    +
    {{ __('web.search.popular_categories') }}
    @foreach ($categories->take(8) as $cat)
    @@ -91,11 +91,11 @@ {{ $fmt($newsrooms->total()) }} {{ $newsrooms->total() === 1 ? 'Newsroom' : 'Newsrooms' }} @if ($term !== '') - für „{{ $term }}" + {{ __('web.search.for_term', ['term' => $term]) }} @endif @if ($term !== '') - Filter zurücksetzen + {{ __('web.newsrooms_page.reset_filter') }} @endif
    @@ -123,18 +123,18 @@

    {{ $company->name }}

    @if ($latest) -
    Letzte Meldung {{ $latest->translatedFormat('j. M Y') }}
    +
    {{ __('web.newsrooms_page.latest_release', ['date' => $latest->translatedFormat('j. M Y')]) }}
    @endif
    - {{ $fmt($company->releases_count ?? 0) }} Meldungen + {{ $fmt($company->releases_count ?? 0) }} {{ __('web.industry_index.releases') }} @if ($isRecentlyActive) - {{ $fmt($company->recent_count) }} / 30 Tage + {{ $fmt($company->recent_count) }} / {{ __('web.newsrooms_page.days_30') }} @endif
    @@ -145,17 +145,17 @@ @else
    -

    Keine Newsrooms gefunden

    +

    {{ __('web.newsrooms_page.none_found') }}

    @if ($term !== '') - Für „{{ $term }}" gibt es derzeit keinen passenden Newsroom. Versuchen Sie einen anderen Suchbegriff. + {{ __('web.newsrooms_page.none_found_term', ['term' => $term]) }} @else - Aktuell sind keine aktiven Newsrooms verfügbar. + {{ __('web.newsrooms_page.none_available') }} @endif

    @if ($term !== '') - Alle Newsrooms anzeigen + {{ __('web.newsrooms.all') }} @endif
    @@ -167,13 +167,13 @@
    -
    Eigener Newsroom
    -

    Bündeln Sie Ihre Pressemitteilungen im eigenen Newsroom.

    -

    Eigene Marken-URL · alle Meldungen zentral · SEO-Vorteil für Ihr Unternehmen · redaktionell geprüft.

    +
    {{ __('web.newsrooms_page.cta_eyebrow') }}
    +

    {{ __('web.newsrooms_page.cta_title') }}

    +

    {{ __('web.newsrooms_page.cta_text') }}

    diff --git a/resources/views/web/preise.blade.php b/resources/views/web/preise.blade.php index 7496906d..4207cc58 100644 --- a/resources/views/web/preise.blade.php +++ b/resources/views/web/preise.blade.php @@ -285,8 +285,8 @@
    - {{-- Abschluss mit vorhandener Editorial-Komponente. --}} - + {{-- Newsletter-Sektion bewusst entfernt: kommt nachgelagert mit echtem + Double-Opt-in (siehe dev/release/entwicklungsplan-release.md). --}} diff --git a/resources/views/web/presseecho.blade.php b/resources/views/web/presseecho.blade.php index d7d7e96f..27f0afac 100644 --- a/resources/views/web/presseecho.blade.php +++ b/resources/views/web/presseecho.blade.php @@ -4,8 +4,13 @@ $brand = config('domains.domains.presseecho.brand', []); @endphp -@section('title', $brand['meta_title'] ?? 'Aktuelle Pressemitteilungen aus Wirtschaft & Industrie – presseecho') -@section('meta_description', $brand['meta_description'] ?? 'Redaktionell kuratierte Pressemitteilungen aus Wirtschaft und Industrie im DACH-Raum.') +{{-- DE-Meta kommt brand-spezifisch aus config/domains.php, EN aus den Übersetzungsdateien. --}} +@section('title', app()->getLocale() === 'de' + ? ($brand['meta_title'] ?? 'Aktuelle Pressemitteilungen aus Wirtschaft & Industrie – presseecho') + : __('web.meta.home_title', ['brand' => ($brand['name'] ?? 'presseecho').($brand['accent'] ?? '')])) +@section('meta_description', app()->getLocale() === 'de' + ? ($brand['meta_description'] ?? 'Qualitätsgeprüfte Pressemitteilungen aus Wirtschaft und Industrie im DACH-Raum.') + : __('web.meta.home_description')) @section('hreflang') @@ -14,7 +19,7 @@ @endsection @section('content') - +
    - + {{-- Newsletter-Sektion bewusst entfernt (Double-Opt-in nachgelagert, + siehe dev/release/entwicklungsplan-release.md) — stattdessen Veröffentlichen-CTA. --}} +
    diff --git a/resources/views/web/pressekonto.blade.php b/resources/views/web/pressekonto.blade.php index f9619ee9..99d49a9e 100644 --- a/resources/views/web/pressekonto.blade.php +++ b/resources/views/web/pressekonto.blade.php @@ -39,7 +39,7 @@

    ist der gemeinsame Publisher-Bereich für - unsere beiden Pressefachportale. Pressemitteilungen schreiben, redaktionell prüfen lassen, auf + unsere beiden Pressefachportale. Pressemitteilungen schreiben, automatisiert prüfen lassen, auf beiden Reichweiten veröffentlichen — und Mitteilungen, Newsrooms und Abrechnung an einem Ort verwalten.

    @@ -234,7 +234,7 @@ Pressemitteilungen auf beiden Portalen

    - Eine Mitteilung schreiben, redaktionell prüfen lassen, auf und gleichzeitig veröffentlichen. Vorschau und Qualitäts-Checkliste inklusive.

    @@ -291,7 +291,7 @@ Aufrufe, Status und Abrechnung zentral

    - Aufrufe je Mitteilung über beide Portale. Prüf-Status und redaktionelles Feedback direkt an der + Aufrufe je Mitteilung über beide Portale. Prüf-Status und Feedback direkt an der Mitteilung, Buchungen und Rechnungen zentral im Konto.

      @@ -326,8 +326,9 @@
    Registrieren

    - Konto in unter zwei Minuten anlegen — mit E-Mail und Passwort oder Google-Konto. Nach - Bestätigung Ihrer E-Mail-Adresse ist das Konto für beide Portale freigeschaltet. + Konto in unter zwei Minuten anlegen — mit E-Mail und Passwort, Anmeldung wahlweise + auch passwortlos per Magic-Link. Nach Bestätigung Ihrer E-Mail-Adresse ist das Konto + für beide Portale freigeschaltet.

  • @@ -539,7 +540,7 @@

    ist nicht „irgendein Tool" — es ist die - zentrale Plattform für unsere beiden redaktionell geführten Pressefachportale. Jedes Portal hat einen + zentrale Plattform für unsere beiden Pressefachportale. Jedes Portal hat einen eigenen Charakter, eigene Leserschaft und eigene Themen-Schwerpunkte.

    @@ -564,7 +565,7 @@

    Themen-Sammlung und Langzeit-Archiv für Pressemitteilungen aus DACH. Editorial-Charakter, - Themen-Tiefe, redaktionell erschlossen — der Ort, an dem eine Mitteilung über Jahre auffindbar + Themen-Tiefe, nach Branchen erschlossen — der Ort, an dem eine Mitteilung über Jahre auffindbar bleibt.


    @@ -577,7 +578,7 @@
    {{ $fmtCount($hubStats['categories']) }} Branchen
    -
    redaktionell erschlossen
    +
    nach Branchen erschlossen
    - Nach der redaktionellen Freigabe geht Ihre Mitteilung sofort live — werktags in der Regel + Nach bestandener Inhaltsprüfung geht Ihre Mitteilung sofort live — werktags in der Regel noch am selben Tag. Alternativ legen Sie einen Wunschtermin fest: Dann erscheint die Mitteilung automatisch zum geplanten Zeitpunkt, geprüft und freigegeben ist sie bereits davor.
    diff --git a/resources/views/web/release-detail.blade.php b/resources/views/web/release-detail.blade.php index 1a36f4bd..6d25cf2c 100644 --- a/resources/views/web/release-detail.blade.php +++ b/resources/views/web/release-detail.blade.php @@ -3,17 +3,15 @@ @php use Illuminate\Support\Str; - \Carbon\Carbon::setLocale('de'); - $company = $release->company; - $categoryTranslation = $release->category?->translations?->firstWhere('locale', 'de') + $categoryTranslation = $release->category?->translations?->firstWhere('locale', app()->getLocale()) ?? $release->category?->translations?->first(); $categoryName = $categoryTranslation?->name; $categorySlug = $categoryTranslation?->slug; $categoryHref = $categorySlug ? route('kategorie', ['slug' => $categorySlug]) : route('web.home'); - $parentTranslation = $release->category?->parent?->translations?->firstWhere('locale', 'de') + $parentTranslation = $release->category?->parent?->translations?->firstWhere('locale', app()->getLocale()) ?? $release->category?->parent?->translations?->first(); $parentName = $parentTranslation?->name; $parentSlug = $parentTranslation?->slug; @@ -38,9 +36,9 @@ $companyName = $company?->name; $companyInitial = $companyName ? mb_strtoupper(mb_substr($companyName, 0, 1)) : '·'; - $companyHref = $company?->slug ? route('newsrooms').'#'.$company->slug : route('newsrooms'); + $companyHref = $company?->slug ? route('newsroom', ['slug' => $company->slug]) : route('newsrooms'); - $countryNames = ['DE' => 'Deutschland', 'AT' => 'Österreich', 'CH' => 'Schweiz']; + $countryNames = ['DE' => __('web.countries.de'), 'AT' => __('web.countries.at'), 'CH' => __('web.countries.ch')]; $companyCountry = $company?->country_code ? ($countryNames[strtoupper($company->country_code)] ?? null) : null; $boilerplate = filled($release->boilerplate_override) ? $release->boilerplate_override : $company?->boilerplate; @@ -84,8 +82,8 @@
    {{-- Breadcrumbs --}}
    -
    @@ -105,7 +103,7 @@ @if ($embargoAt)
    - ● Embargo aufgehoben + ● {{ __('web.detail.embargo_lifted') }} {{ $embargoAt->translatedFormat('j. F Y, H:i') }}
    @@ -116,7 +114,7 @@ {{ $categoryName }} @endif - Für Wirtschaftsjournalisten + {{ __('web.detail.audience') }}

    @@ -139,10 +137,10 @@ @endif @if ($publishedAt) @endif - {{ $readTime }} Min. Lesezeit · {{ number_format($wordCount, 0, ',', '.') }} Wörter + {{ __('web.shared.read_time', ['minutes' => $readTime]) }} · {{ __('web.detail.words', ['count' => number_format($wordCount, 0, ',', '.')]) }} @if ($companyCountry) @@ -152,12 +150,12 @@ @if ($isRecommended) + title="{{ __('web.detail.recommended_hint') }}"> - Redaktionsempfehlung + {{ __('web.detail.recommended') }} @endif @@ -172,13 +170,13 @@ class="absolute inset-0 h-full w-full object-cover" loading="eager"> @else - {{ $companyName ? 'Bild · Pressefoto '.$companyName : 'Bild · Pressefoto' }} (16:9) + {{ $companyName ? __('web.detail.image_placeholder_company', ['company' => $companyName]) : __('web.detail.image_placeholder') }} (16:9) @endif @if ($imageCaption || $imageCredit)
    - {{ $imageCaption }}@if ($imageCaption && $imageCredit) · @endif@if ($imageCredit)Foto: {{ $imageCredit }}@endif + {{ $imageCaption }}@if ($imageCaption && $imageCredit) · @endif@if ($imageCredit){{ __('web.detail.photo') }}: {{ $imageCredit }}@endif
    @endif @@ -193,45 +191,20 @@ @if (filled($boilerplate))
    -
    Über das Unternehmen
    +
    {{ __('web.detail.about_company') }}

    {{ $boilerplate }}

    @endif - {{-- Inline Ad --}} - - {{-- Pressekontakt --}} @if ($contact)
    -
    Pressekontakt
    +
    {{ __('web.detail.press_contact') }}
    {{ $contactName ?: $companyName }} @if ($publisherVerified) - +
    - Sie haben ein rechtliches Anliegen zu dieser Mitteilung? + {{ __('web.detail.legal_question') }} - Inhalt melden + {{ __('web.detail.report_content') }} - Eigene Daten entfernen + {{ __('web.detail.remove_data') }} - Meldungen sind kostenlos. + {{ __('web.detail.reports_free') }}
    @@ -295,7 +268,7 @@ {{-- Publisher --}} @if ($companyName)
    -
    Veröffentlicht von
    +
    {{ __('web.detail.published_by') }}
    {{ $companyInitial }}
    @@ -308,18 +281,17 @@ @if ($publisherVerified)
    + title="{{ __('web.detail.verified_by', ['brand' => $brandLabel]) }}"> - Verifizierter Publisher + {{ __('web.detail.verified_publisher') }}
    @endif
    @endif @@ -331,7 +303,7 @@ copied: false, copy() { navigator.clipboard?.writeText(this.url).then(() => { this.copied = true; setTimeout(() => this.copied = false, 2000); }); } }"> -
    Teilen
    +
    {{ __('web.detail.share') }}
    @@ -343,11 +315,11 @@ - E-Mail + {{ __('web.detail.email') }}
    @@ -355,7 +327,7 @@ {{-- Schlagwörter --}} @if ($keywords->isNotEmpty())
    -
    Schlagwörter
    +
    {{ __('web.detail.keywords') }}
    @foreach ($keywords as $keyword) {{ $keyword }} @@ -364,40 +336,26 @@
    @endif - {{-- Newsletter --}} -
    -
    Newsletter
    -

    - {{ $categoryName ?: 'Wirtschaft' }} · ab Score 80 -

    -

    - Höchstens 2 Mails pro Woche. Nur Meldungen ab Content-Score 80. -

    - Abonnieren → -
    + {{-- Newsletter-Box bewusst entfernt: kommt nachgelagert mit echtem + Double-Opt-in (siehe dev/release/entwicklungsplan-release.md). --}} {{-- Weiterführend --}}
    -
    Weiterführend
    +
    {{ __('web.detail.further') }}
    @@ -411,26 +369,26 @@
    @if ($companyReleases->isNotEmpty())
    -

    Mehr von {{ $companyName }}

    -
    Aktuelle Mitteilungen aus diesem Newsroom
    +

    {{ __('web.detail.more_from', ['company' => $companyName]) }}

    +
    {{ __('web.detail.more_from_sub') }}

    @foreach ($companyReleases as $companyRelease) @endforeach - Alle Mitteilungen von {{ $companyName }} → + {{ __('web.detail.all_from', ['company' => $companyName]) }} →
    @endif @if ($relatedReleases->isNotEmpty())
    -

    Verwandte Meldungen

    -
    Reaktionen, ähnliche Themen, Branchenkontext
    +

    {{ __('web.detail.related') }}

    +
    {{ __('web.detail.related_sub') }}

    @foreach ($relatedReleases as $relatedRelease) @endforeach @if ($categoryName) - Alle Meldungen aus {{ $categoryName }} → + {{ __('web.detail.all_from_category', ['category' => $categoryName]) }} → @endif
    @endif diff --git a/resources/views/web/suche.blade.php b/resources/views/web/suche.blade.php index 51c4303e..2c66ab23 100644 --- a/resources/views/web/suche.blade.php +++ b/resources/views/web/suche.blade.php @@ -6,8 +6,8 @@ $brandAccent = $brandConfig['accent'] ?? '24'; @endphp -@section('title', 'Pressemitteilungen durchsuchen – '.$brandName.$brandAccent) -@section('meta_description', 'Durchsuchen Sie das geprüfte Pressemitteilungs-Archiv nach Stichwort, Unternehmen und Branche – redaktionell geprüft und dauerhaft auffindbar im DACH-Raum.') +@section('title', __('web.search.meta_title', ['brand' => $brandName.$brandAccent])) +@section('meta_description', __('web.search.meta_description')) @section('hreflang') @@ -24,19 +24,19 @@
    {{ $brandName }}{{ $brandAccent }} - Suche + {{ __('web.header.search_eyebrow') }}
    {{-- Masthead --}}
    -
    Archiv-Suche
    +
    {{ __('web.search.eyebrow') }}

    - Pressemitteilungen durchsuchen + {{ __('web.header.search_title') }}

    - Stichwort, Unternehmen oder Branche – durchsuchen Sie das vollständige, redaktionell geprüfte Archiv. + {{ __('web.search.intro') }}


    diff --git a/resources/views/web/veroeffentlichen.blade.php b/resources/views/web/veroeffentlichen.blade.php index 14757853..fbd067ad 100644 --- a/resources/views/web/veroeffentlichen.blade.php +++ b/resources/views/web/veroeffentlichen.blade.php @@ -27,9 +27,10 @@ @section('title', 'Pressemitteilung veröffentlichen – '.$brandName.$brandAccent) @section('meta_description', 'Pressemitteilungen geprüft veröffentlichen und dauerhaft auffindbar im Archiv. Faire Preise, keine Vertragsbindung – Einreichung über den zentralen Publisher-Bereich.') +{{-- Kein hreflang="en": Der Seiteninhalt liegt bisher nur auf Deutsch vor. + EN-Übersetzung der Langform-Seiten ist nachgelagert (siehe Release-Plan). --}} @section('hreflang') - @endsection @@ -48,7 +49,7 @@

    - Die redaktionelle Sichtung sichert den Charakter der Plattform. Mitteilungen, die diese Schwelle nicht + Die Inhaltsprüfung sichert den Charakter der Plattform. Mitteilungen, die diese Schwelle nicht erreichen, werden mit einer kurzen Begründung zurückgegeben — meist mit einem konkreten Korrekturvorschlag.


    @@ -382,7 +383,7 @@

    Sie finden hier keine Antwort?

    - Redaktion direkt anschreiben + Direkt Kontakt aufnehmen
    Antwort werktags per E-Mail
    @@ -419,7 +420,7 @@
    Mitteilung einreichen · Schritt eins

    Bereit, Ihre Mitteilung einzureichen?

    - Konto in zwei Minuten anlegen, Text und Bild einreichen, nach redaktioneller Sichtung veröffentlicht. + Konto in zwei Minuten anlegen, Text und Bild einreichen, nach bestandener Inhaltsprüfung veröffentlicht. Einmaliges Konto — beide Portale (businessportal24 & presseecho.de) nutzbar.

    diff --git a/routes/console.php b/routes/console.php index 6eb47e7f..f53a5cc8 100644 --- a/routes/console.php +++ b/routes/console.php @@ -4,6 +4,7 @@ use App\Console\Commands\GenerateManualInvoices; use App\Console\Commands\PublishScheduledPressReleases; use App\Console\Commands\PurgeExpiredPressReleaseDrafts; use App\Console\Commands\PurgeMagicLinks; +use App\Console\Commands\RefreshMarketData; use App\Console\Commands\ResetMonthlyPressReleaseQuota; use Illuminate\Foundation\Inspiring; use Illuminate\Support\Facades\Artisan; @@ -63,3 +64,14 @@ Schedule::command(ResetMonthlyPressReleaseQuota::class) ->monthlyOn(1, '00:05') ->withoutOverlapping() ->runInBackground(); + +// ======================================== +// Börsendaten für den Web-Ticker +// ======================================== + +// Kurse (DAX, EUR/USD, Brent) alle 15 Minuten aktualisieren; die Views lesen +// nur aus dem Cache — ein API-Ausfall leert die Anzeige nicht sofort. +Schedule::command(RefreshMarketData::class) + ->everyFifteenMinutes() + ->withoutOverlapping() + ->runInBackground(); diff --git a/tests/Feature/Web/Businessportal24HomeTest.php b/tests/Feature/Web/Businessportal24HomeTest.php index 04699c24..11d348c7 100644 --- a/tests/Feature/Web/Businessportal24HomeTest.php +++ b/tests/Feature/Web/Businessportal24HomeTest.php @@ -7,8 +7,32 @@ use App\Models\Company; use App\Models\PressRelease; use Tests\TestCase; -test('businessportal24 homepage renders the editorial shell', function () { +test('businessportal24 homepage renders the editorial shell with real data', function () { /** @var TestCase $this */ + $category = Category::factory()->create([ + 'portal' => Portal::Businessportal24, + ]); + $category->translations()->create([ + 'locale' => 'de', + 'name' => 'Energie & Klima', + 'slug' => 'energie-klima', + ]); + + $company = Company::factory()->businessportal24()->create([ + 'name' => 'Muster Energie GmbH', + ]); + + PressRelease::factory() + ->published() + ->forPortal(Portal::Businessportal24) + ->for($category) + ->for($company) + ->create([ + 'title' => 'Shell-Test Meldung', + 'slug' => 'shell-test-meldung', + 'published_at' => now()->subHour(), + ]); + $this->get('https://businessportal24.test/de') ->assertSuccessful() ->assertSee('businessportal', false) @@ -16,19 +40,69 @@ test('businessportal24 homepage renders the editorial shell', function () { ->assertSeeText('Aktuelle Meldungen') ->assertSeeText('Im Fokus') ->assertSeeText('Aktive Newsrooms') - ->assertSeeText('Bleiben Sie informiert') ->assertSeeText('Veröffentlichen Sie Ihre Pressemitteilung') - ->assertSeeText('Redaktioneller Qualitätsstandard') + ->assertSeeText('Ihre Pressemitteilung, qualitätsgeprüft veröffentlicht') + ->assertSeeText('Konto anlegen') + ->assertSeeText('Unser Qualitätsstandard') + ->assertDontSeeText('redaktionell geprüft') ->assertSeeText('AD-HOC') ->assertSeeText('Branchen-Index') ->assertSeeText('Heute im Fokus') - ->assertSee('businessportal', false) ->assertSee('>24<', false) ->assertDontSeeText('führende Plattform') ->assertDontSeeText('maximale Reichweite') ->assertDontSeeText('Exklusiv-Interview'); }); +test('the ad-hoc ticker also renders on subpages', function () { + /** @var TestCase $this */ + $category = Category::factory()->create([ + 'portal' => Portal::Businessportal24, + ]); + $category->translations()->create([ + 'locale' => 'de', + 'name' => 'Energie & Klima', + 'slug' => 'energie-klima', + ]); + + $company = Company::factory()->businessportal24()->create([ + 'name' => 'Muster Energie GmbH', + ]); + + PressRelease::factory() + ->published() + ->forPortal(Portal::Businessportal24) + ->for($category) + ->for($company) + ->create([ + 'title' => 'Ticker-Meldung auf Unterseite', + 'slug' => 'ticker-meldung-auf-unterseite', + 'published_at' => now()->subHour(), + ]); + + $this->get('https://businessportal24.test/de/newsrooms') + ->assertSuccessful() + ->assertSeeText('AD-HOC') + ->assertSeeText('Ticker-Meldung auf Unterseite'); + + $this->get('https://businessportal24.test/de/preise') + ->assertSuccessful() + ->assertSeeText('AD-HOC') + ->assertSeeText('Ticker-Meldung auf Unterseite'); +}); + +test('businessportal24 homepage hides data sections and shows the empty feed state without content', function () { + /** @var TestCase $this */ + $this->get('https://businessportal24.test/de') + ->assertSuccessful() + ->assertSeeText('Aktuelle Meldungen') + ->assertSeeText('Aktuell liegen keine Meldungen vor') + ->assertDontSeeText('Im Fokus') + ->assertDontSeeText('Aktive Newsrooms') + ->assertDontSeeText('Branchen-Index') + ->assertDontSeeText('Heute im Fokus'); +}); + test('businessportal24 homepage feed only shows published businessportal content', function () { /** @var TestCase $this */ $category = Category::factory()->create([ diff --git a/tests/Feature/Web/EditionRoutingTest.php b/tests/Feature/Web/EditionRoutingTest.php index 6af62dbd..ad95bc36 100644 --- a/tests/Feature/Web/EditionRoutingTest.php +++ b/tests/Feature/Web/EditionRoutingTest.php @@ -23,6 +23,22 @@ test('both editions render the homepage', function () { $this->get('https://businessportal24.test/en')->assertSuccessful(); }); +test('the english edition renders a localized ui', function () { + /** @var TestCase $this */ + $this->get('https://businessportal24.test/en') + ->assertSuccessful() + ->assertSeeText('Latest releases') + ->assertSeeText('Publish your press release') + ->assertSeeText('Our quality standard') + ->assertDontSeeText('Aktuelle Meldungen') + ->assertDontSeeText('Unser Qualitätsstandard'); + + $this->get('https://businessportal24.test/de') + ->assertSuccessful() + ->assertSeeText('Aktuelle Meldungen') + ->assertDontSeeText('Latest releases'); +}); + test('an unsupported edition prefix returns 404', function () { /** @var TestCase $this */ $this->get('https://businessportal24.test/fr')->assertNotFound(); diff --git a/tests/Feature/Web/MarketDataTest.php b/tests/Feature/Web/MarketDataTest.php new file mode 100644 index 00000000..30a0a277 --- /dev/null +++ b/tests/Feature/Web/MarketDataTest.php @@ -0,0 +1,98 @@ +set('market.enabled', true); + config()->set('market.symbols', [ + ['label' => 'DAX', 'symbol' => '^GDAXI', 'precision' => 0, 'suffix' => ''], + ['label' => 'EUR/USD', 'symbol' => 'EURUSD=X', 'precision' => 2, 'suffix' => ''], + ]); +}); + +function fakeYahooChart(float $price, float $previousClose): array +{ + return [ + 'chart' => [ + 'result' => [ + ['meta' => ['regularMarketPrice' => $price, 'chartPreviousClose' => $previousClose]], + ], + ], + ]; +} + +it('holt Kurse, berechnet Deltas und cacht die Slides', function () { + Http::fake([ + '*GDAXI*' => Http::response(fakeYahooChart(24150.0, 24000.0)), + '*EURUSD*' => Http::response(fakeYahooChart(1.0815, 1.0902)), + ]); + + $slides = app(MarketDataService::class)->refresh(); + + expect($slides)->toHaveCount(2) + ->and($slides[0]['label'])->toBe('DAX') + ->and($slides[0]['value'])->toBe('24.150') + ->and($slides[0]['delta'])->toBe('+0,6%') + ->and($slides[0]['positive'])->toBeTrue() + ->and($slides[1]['delta'])->toBe('-0,8%') + ->and($slides[1]['positive'])->toBeFalse(); + + Http::fake(); + + expect(app(MarketDataService::class)->slides())->toBe($slides); +}); + +it('behält den Cache-Stand, wenn der Abruf komplett fehlschlägt', function () { + Cache::put((string) config('market.cache_key'), [ + ['label' => 'DAX', 'value' => '24.150', 'delta' => '+0,6%', 'positive' => true], + ], 3600); + + Http::fake(['*' => Http::response(null, 500)]); + + $slides = app(MarketDataService::class)->refresh(); + + expect($slides)->toHaveCount(1) + ->and($slides[0]['value'])->toBe('24.150'); +}); + +it('liefert keine Slides, wenn Marktdaten deaktiviert sind', function () { + config()->set('market.enabled', false); + + Cache::put((string) config('market.cache_key'), [ + ['label' => 'DAX', 'value' => '24.150', 'delta' => '+0,6%', 'positive' => true], + ], 3600); + + expect(app(MarketDataService::class)->slides())->toBe([]); +}); + +it('überspringt Symbole mit unbrauchbarer API-Antwort', function () { + Http::fake([ + '*GDAXI*' => Http::response(['chart' => ['result' => [['meta' => []]]]]), + '*EURUSD*' => Http::response(fakeYahooChart(1.0815, 1.0902)), + ]); + + $slides = app(MarketDataService::class)->refresh(); + + expect($slides)->toHaveCount(1) + ->and($slides[0]['label'])->toBe('EUR/USD'); +}); + +it('aktualisiert Kurse über den Artisan-Command', function () { + Http::fake(['*' => Http::response(fakeYahooChart(24150.0, 24000.0))]); + + $this->artisan('market:refresh') + ->expectsOutputToContain('DAX') + ->assertSuccessful(); + + expect(app(MarketDataService::class)->slides())->not->toBeEmpty(); +}); + +it('meldet einen Fehlschlag des Commands ohne Kursdaten', function () { + Http::fake(['*' => Http::response(null, 500)]); + + $this->artisan('market:refresh')->assertFailed(); +}); diff --git a/tests/Feature/Web/PresseechoHomeTest.php b/tests/Feature/Web/PresseechoHomeTest.php index 11acb551..286c81db 100644 --- a/tests/Feature/Web/PresseechoHomeTest.php +++ b/tests/Feature/Web/PresseechoHomeTest.php @@ -7,8 +7,32 @@ use App\Models\Company; use App\Models\PressRelease; use Tests\TestCase; -test('presseecho homepage renders the editorial shell', function () { +test('presseecho homepage renders the editorial shell with real data', function () { /** @var TestCase $this */ + $category = Category::factory()->create([ + 'portal' => Portal::Presseecho, + ]); + $category->translations()->create([ + 'locale' => 'de', + 'name' => 'Energie & Klima', + 'slug' => 'energie-klima', + ]); + + $company = Company::factory()->presseecho()->create([ + 'name' => 'Muster Energie GmbH', + ]); + + PressRelease::factory() + ->published() + ->forPortal(Portal::Presseecho) + ->for($category) + ->for($company) + ->create([ + 'title' => 'Shell-Test Meldung', + 'slug' => 'shell-test-meldung', + 'published_at' => now()->subHour(), + ]); + $this->get('https://presseecho.test/de') ->assertSuccessful() ->assertSee('presseecho', false) @@ -16,14 +40,28 @@ test('presseecho homepage renders the editorial shell', function () { ->assertSeeText('Aktuelle Meldungen') ->assertSeeText('Im Fokus') ->assertSeeText('Aktive Newsrooms') - ->assertSeeText('Bleiben Sie informiert') ->assertSeeText('Veröffentlichen Sie Ihre Pressemitteilung') - ->assertSeeText('Redaktioneller Qualitätsstandard') + ->assertSeeText('Ihre Pressemitteilung, qualitätsgeprüft veröffentlicht') + ->assertSeeText('Konto anlegen') + ->assertSeeText('Unser Qualitätsstandard') + ->assertDontSeeText('redaktionell geprüft') ->assertSeeText('AD-HOC') ->assertSeeText('Branchen-Index') ->assertSeeText('Heute im Fokus'); }); +test('presseecho homepage hides data sections and shows the empty feed state without content', function () { + /** @var TestCase $this */ + $this->get('https://presseecho.test/de') + ->assertSuccessful() + ->assertSeeText('Aktuelle Meldungen') + ->assertSeeText('Aktuell liegen keine Meldungen vor') + ->assertDontSeeText('Im Fokus') + ->assertDontSeeText('Aktive Newsrooms') + ->assertDontSeeText('Branchen-Index') + ->assertDontSeeText('Heute im Fokus'); +}); + test('presseecho homepage feed only shows published presseecho content', function () { /** @var TestCase $this */ $category = Category::factory()->create([