Files
KantSites/src/Twig/LanguageExtension.php
T
kantarciandClaude Opus 5.5 47292c7266 Finish an archive update in one go and translate Kantarci Reisen into Turkish
The request that swaps the files now ends with a plain page instead of running on through the
framework with old code in memory and new code on disk - that is what answered with a 500 after
a successful update. The page calls the second step itself with a one-time key: migrations,
clean-up, maintenance off, back to the system screen (ADR 112).

Turkish is complete in the fixed template texts and form messages. Two language switches on one
page get distinct landmark names. Kantarci Reisen gets a Turkish version of every page, the
header, footer, menu and enquiry form, with a DE · TR switch; the reset script applies it.
The DM Sans 600 files are needed after all and are committed with fonts.css.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:36:25 +02:00

219 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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',
'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',
'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;
}
}