The pause and play labels were missing from the kant_t dictionary, so English and Turkish pages showed them in German. video.js now skips a data-video-loop without its toggle instead of throwing and leaving every later video still, and the design panel puts size and spacing first for the video block. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
225 lines
13 KiB
PHP
225 lines
13 KiB
PHP
<?php
|
||
|
||
namespace App\Twig;
|
||
|
||
use App\Content\Languages;
|
||
use Symfony\Component\HttpFoundation\RequestStack;
|
||
use Twig\Extension\AbstractExtension;
|
||
use Twig\TwigFilter;
|
||
use Twig\TwigFunction;
|
||
|
||
/** Sprachangaben für die Vorlagen (ADR 111). */
|
||
final class LanguageExtension extends AbstractExtension
|
||
{
|
||
/**
|
||
* Die festen Texte der öffentlichen Vorlagen, die kein Redakteur pflegt: Sprunglink,
|
||
* Beschriftungen von Navigationen, Einwilligung, Beitragslisten. Schlüssel ist der deutsche
|
||
* Text. Für Sprachen ohne eigene Liste gilt Englisch - auf einer französischen Seite ist
|
||
* „Skip to content“ näher am Besucher als „Zum Inhalt springen“.
|
||
*/
|
||
public const UI = [
|
||
'en' => [
|
||
'Zum Inhalt springen' => 'Skip to content',
|
||
'Menü' => 'Menu',
|
||
'Brotkrumen' => 'Breadcrumb',
|
||
'Start' => 'Home',
|
||
'Soziale Profile' => 'Social profiles',
|
||
'Zum Blättern seitwärts scrollen' => 'Scroll sideways to browse',
|
||
'Anbieter:' => 'Provider:',
|
||
'Inhalt laden und einwilligen' => 'Load content and consent',
|
||
'Einwilligung ändern' => 'Change consent',
|
||
'Bildergalerie' => 'Image gallery',
|
||
'Bild' => 'Image',
|
||
'vergrößern' => 'enlarge',
|
||
'Video' => 'Video',
|
||
'Video anhalten' => 'Pause video',
|
||
'Video abspielen' => 'Play video',
|
||
'Ansehen' => 'View',
|
||
'Weiterlesen' => 'Read more',
|
||
'Weitere Beiträge' => 'More posts',
|
||
'Vorheriger Beitrag' => 'Previous post',
|
||
'Nächster Beitrag' => 'Next post',
|
||
'Alle akzeptieren' => 'Accept all',
|
||
'Nur notwendige' => 'Necessary only',
|
||
'Einzeln wählen' => 'Choose individually',
|
||
'Auswahl speichern' => 'Save selection',
|
||
'Datenschutz' => 'Privacy',
|
||
'Impressum' => 'Legal notice',
|
||
'Hauptnavigation' => 'Main navigation',
|
||
'Zweite Navigation' => 'Secondary navigation',
|
||
'Fußzeile' => 'Footer',
|
||
'Rechtliches' => 'Legal',
|
||
'Navigation' => 'Navigation',
|
||
'Nach oben' => 'Back to top',
|
||
'Sprache' => 'Language',
|
||
'Sprache wählen' => 'Choose language',
|
||
// Formulare
|
||
'Dieses Formular ist nicht mehr vorhanden.' => 'This form is no longer available.',
|
||
'Bitte prüfe 1 Eingabe. Die betroffenen Felder sind unten gekennzeichnet.' => 'Please check 1 entry. The field concerned is marked below.',
|
||
'Bitte prüfe %d Eingaben. Die betroffenen Felder sind unten gekennzeichnet.' => 'Please check %d entries. The fields concerned are marked below.',
|
||
'Pflichtfeld' => 'required',
|
||
'in' => 'in',
|
||
'bitte wählen' => 'please choose',
|
||
'Zum Absenden dieses Formulars wird JavaScript benötigt. Schreib uns sonst gern direkt eine E-Mail.' => 'Sending this form requires JavaScript. Otherwise, feel free to email us directly.',
|
||
'Zusammenfassung' => 'Summary',
|
||
'Bitte prüfen Sie Ihre Angaben. Über „Ändern“ kommen Sie zum jeweiligen Schritt zurück.' => 'Please check your details. “Change” takes you back to the step.',
|
||
'Schritte des Formulars' => 'Form steps',
|
||
'Schritt' => 'Step',
|
||
'Zurück' => 'Back',
|
||
'Weiter' => 'Next',
|
||
'Ändern' => 'Change',
|
||
'Ja' => 'Yes',
|
||
'Bitte wähle mindestens eine Option.' => 'Please choose at least one option.',
|
||
'Absenden' => 'Send',
|
||
'Vielen Dank. Deine Nachricht ist angekommen.' => 'Thank you. Your message has arrived.',
|
||
// Beiträge und Archive
|
||
'Beiträge' => 'Posts',
|
||
'Beitrag' => 'post',
|
||
'Kategorie' => 'Category',
|
||
'Schlagwort' => 'Tag',
|
||
'Alle Beiträge, die neuesten zuerst.' => 'All posts, newest first.',
|
||
'Beiträge in der Kategorie „%s“.' => 'Posts in the category “%s”.',
|
||
'Beiträge zum Schlagwort „%s“.' => 'Posts tagged “%s”.',
|
||
'von' => 'by',
|
||
'← Neuere' => '← Newer',
|
||
'Ältere →' => 'Older →',
|
||
'Seite %d von %d' => 'Page %d of %d',
|
||
'Externe Inhalte und Einwilligung' => 'External content and consent',
|
||
'Diese Website lädt bestimmte Inhalte erst, wenn du zustimmst. Ohne Zustimmung wird die Seite vollständig angezeigt, nur eingebettete Inhalte anderer Anbieter bleiben leer.' => 'This website only loads certain content once you agree. Without consent the page is shown in full; only embedded content from other providers stays empty.',
|
||
'Statistik' => 'Statistics',
|
||
'Externe Inhalte' => 'External content',
|
||
'Messung, wie die Website benutzt wird.' => 'Measuring how the website is used.',
|
||
'Inhalte von anderen Anbietern, etwa Karten, Videos, Bewertungen oder ein Chat. Sie sehen die Adresse dieser Seite und deine IP-Adresse, sobald sie geladen werden.' => 'Content from other providers, such as maps, videos, reviews or a chat. They see the address of this page and your IP address as soon as they load.',
|
||
// 404 und Einwilligung
|
||
'Seite nicht gefunden' => 'Page not found',
|
||
'Diese Seite ist nicht mehr verfügbar.' => 'This page is no longer available.',
|
||
'Die gesuchte Seite ist nicht verfügbar. Über die Startseite findest du wieder zum richtigen Inhalt.' => 'The page you are looking for is not available. The home page will take you back to the right content.',
|
||
'Die Adresse ist möglicherweise veraltet oder wurde falsch eingegeben. Über die Startseite findest du wieder zum richtigen Inhalt.' => 'The address may be out of date or mistyped. The home page will take you back to the right content.',
|
||
'Zur Startseite' => 'To the home page',
|
||
],
|
||
'fr' => [
|
||
'Zum Inhalt springen' => 'Aller au contenu',
|
||
'Menü' => 'Menu',
|
||
'Start' => 'Accueil',
|
||
'Datenschutz' => 'Confidentialité',
|
||
'Impressum' => 'Mentions légales',
|
||
'Hauptnavigation' => 'Navigation principale',
|
||
'Nach oben' => 'Haut de page',
|
||
'Weiterlesen' => 'Lire la suite',
|
||
'Alle akzeptieren' => 'Tout accepter',
|
||
'Nur notwendige' => 'Uniquement nécessaires',
|
||
'Sprache' => 'Langue',
|
||
'Sprache wählen' => 'Choisir la langue',
|
||
],
|
||
'tr' => [
|
||
'Zum Inhalt springen' => 'İçeriğe geç',
|
||
'Menü' => 'Menü',
|
||
'Start' => 'Ana sayfa',
|
||
'Datenschutz' => 'Gizlilik',
|
||
'Impressum' => 'Künye',
|
||
'Hauptnavigation' => 'Ana menü',
|
||
'Nach oben' => 'Yukarı',
|
||
'Weiterlesen' => 'Devamını oku',
|
||
'Alle akzeptieren' => 'Tümünü kabul et',
|
||
'Nur notwendige' => 'Yalnızca gerekli',
|
||
'Sprache' => 'Dil',
|
||
'Sprache wählen' => 'Dil seçin',
|
||
// Vollständig, damit eine türkische Seite nirgends auf Englisch ausweicht (09.10.2026).
|
||
'Brotkrumen' => 'Sayfa yolu',
|
||
'Soziale Profile' => 'Sosyal medya',
|
||
'Zum Blättern seitwärts scrollen' => 'Gezinmek için yana kaydırın',
|
||
'Anbieter:' => 'Sağlayıcı:',
|
||
'Inhalt laden und einwilligen' => 'İçeriği yükle ve onayla',
|
||
'Einwilligung ändern' => 'Onayı değiştir',
|
||
'Bildergalerie' => 'Resim galerisi',
|
||
'Bild' => 'Resim',
|
||
'vergrößern' => 'büyüt',
|
||
'Video' => 'Video',
|
||
'Video anhalten' => 'Videoyu durdur',
|
||
'Video abspielen' => 'Videoyu oynat',
|
||
'Ansehen' => 'İncele',
|
||
'Weitere Beiträge' => 'Diğer yazılar',
|
||
'Vorheriger Beitrag' => 'Önceki yazı',
|
||
'Nächster Beitrag' => 'Sonraki yazı',
|
||
'Einzeln wählen' => 'Tek tek seç',
|
||
'Auswahl speichern' => 'Seçimi kaydet',
|
||
'Zweite Navigation' => 'İkinci menü',
|
||
'Fußzeile' => 'Alt bilgi',
|
||
'Rechtliches' => 'Yasal bilgiler',
|
||
'Navigation' => 'Menü',
|
||
'Dieses Formular ist nicht mehr vorhanden.' => 'Bu form artık mevcut değil.',
|
||
'Bitte prüfe 1 Eingabe. Die betroffenen Felder sind unten gekennzeichnet.' => 'Lütfen 1 girişi kontrol edin. İlgili alan aşağıda işaretlenmiştir.',
|
||
'Bitte prüfe %d Eingaben. Die betroffenen Felder sind unten gekennzeichnet.' => 'Lütfen %d girişi kontrol edin. İlgili alanlar aşağıda işaretlenmiştir.',
|
||
'Pflichtfeld' => 'zorunlu alan',
|
||
'in' => '',
|
||
'bitte wählen' => 'lütfen seçin',
|
||
'Zum Absenden dieses Formulars wird JavaScript benötigt. Schreib uns sonst gern direkt eine E-Mail.' => 'Bu formu göndermek için JavaScript gereklidir. Dilerseniz bize doğrudan e-posta gönderebilirsiniz.',
|
||
'Zusammenfassung' => 'Özet',
|
||
'Bitte prüfen Sie Ihre Angaben. Über „Ändern“ kommen Sie zum jeweiligen Schritt zurück.' => 'Lütfen bilgilerinizi kontrol edin. „Değiştir“ ile ilgili adıma geri dönebilirsiniz.',
|
||
'Schritte des Formulars' => 'Form adımları',
|
||
'Schritt' => 'Adım',
|
||
'Zurück' => 'Geri',
|
||
'Weiter' => 'İleri',
|
||
'Ändern' => 'Değiştir',
|
||
'Ja' => 'Evet',
|
||
'Bitte wähle mindestens eine Option.' => 'Lütfen en az bir seçenek seçin.',
|
||
'Absenden' => 'Gönder',
|
||
'Vielen Dank. Deine Nachricht ist angekommen.' => 'Teşekkürler. Mesajınız bize ulaştı.',
|
||
'Beiträge' => 'Yazılar',
|
||
'Beitrag' => 'yazı',
|
||
'Kategorie' => 'Kategori',
|
||
'Schlagwort' => 'Etiket',
|
||
'Alle Beiträge, die neuesten zuerst.' => 'Tüm yazılar, en yenisi önce.',
|
||
'Beiträge in der Kategorie „%s“.' => '„%s“ kategorisindeki yazılar.',
|
||
'Beiträge zum Schlagwort „%s“.' => '„%s“ etiketli yazılar.',
|
||
'von' => 'yazan:',
|
||
'← Neuere' => '← Daha yeni',
|
||
'Ältere →' => 'Daha eski →',
|
||
'Seite %d von %d' => 'Sayfa %d / %d',
|
||
'Externe Inhalte und Einwilligung' => 'Harici içerikler ve onay',
|
||
'Diese Website lädt bestimmte Inhalte erst, wenn du zustimmst. Ohne Zustimmung wird die Seite vollständig angezeigt, nur eingebettete Inhalte anderer Anbieter bleiben leer.' => 'Bu web sitesi bazı içerikleri ancak onayınızdan sonra yükler. Onay vermeseniz de sayfa eksiksiz görüntülenir; yalnızca diğer sağlayıcıların yerleştirilmiş içerikleri boş kalır.',
|
||
'Statistik' => 'İstatistik',
|
||
'Externe Inhalte' => 'Harici içerikler',
|
||
'Messung, wie die Website benutzt wird.' => 'Web sitesinin nasıl kullanıldığının ölçülmesi.',
|
||
'Inhalte von anderen Anbietern, etwa Karten, Videos, Bewertungen oder ein Chat. Sie sehen die Adresse dieser Seite und deine IP-Adresse, sobald sie geladen werden.' => 'Harita, video, değerlendirme veya sohbet gibi diğer sağlayıcıların içerikleri. Yüklendiklerinde bu sayfanın adresini ve IP adresinizi görürler.',
|
||
'Seite nicht gefunden' => 'Sayfa bulunamadı',
|
||
'Diese Seite ist nicht mehr verfügbar.' => 'Bu sayfa artık mevcut değil.',
|
||
'Die gesuchte Seite ist nicht verfügbar. Über die Startseite findest du wieder zum richtigen Inhalt.' => 'Aradığınız sayfa mevcut değil. Ana sayfadan doğru içeriğe ulaşabilirsiniz.',
|
||
'Die Adresse ist möglicherweise veraltet oder wurde falsch eingegeben. Über die Startseite findest du wieder zum richtigen Inhalt.' => 'Adres eskimiş veya yanlış yazılmış olabilir. Ana sayfadan doğru içeriğe ulaşabilirsiniz.',
|
||
'Zur Startseite' => 'Ana sayfaya',
|
||
],
|
||
];
|
||
|
||
public function __construct(private RequestStack $requests)
|
||
{
|
||
}
|
||
|
||
public function getFunctions(): array
|
||
{
|
||
return [
|
||
new TwigFunction('kant_og_locale', Languages::ogLocale(...)),
|
||
];
|
||
}
|
||
|
||
public function getFilters(): array
|
||
{
|
||
return [
|
||
new TwigFilter('kant_t', $this->translate(...)),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Ein fester Text in der Sprache der Seite. Ohne weitere Sprache - oder in der Hauptsprache -
|
||
* bleibt er, wie er ist; ebenso jeder Text, der nicht in der Liste steht.
|
||
*/
|
||
public function translate(string $text): string
|
||
{
|
||
$locale = $this->requests->getCurrentRequest()?->attributes->get('kant_locale');
|
||
if (!is_string($locale) || 'de' === $locale) {
|
||
return $text;
|
||
}
|
||
|
||
return self::UI[$locale][$text] ?? self::UI['en'][$text] ?? $text;
|
||
}
|
||
}
|