From 47292c726608ee88a854a394f5d08e48e592ca0d Mon Sep 17 00:00:00 2001 From: Umut Kantarci Date: Fri, 9 Oct 2026 17:36:25 +0200 Subject: [PATCH] Finish an archive update in one go and translate Kantarci Reisen into Turkish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- docs/ARCHITECTURE.md | 12 + docs/STATUS.md | 16 + .../assets/fonts/dm-sans-600-latin-ext.woff2 | Bin 0 -> 18228 bytes public/assets/fonts/dm-sans-600-latin.woff2 | Bin 0 -> 36932 bytes public/assets/fonts/fonts.css | 8 +- scripts/reset-kantarci-reisen.sh | 2 + scripts/translate-kantarci-reisen.php | 381 ++++++++++++++++++ src/Content/FormRepository.php | 14 + src/Controller/SystemController.php | 76 +++- src/Ops/ArchiveUpdate.php | 9 +- src/Twig/LanguageExtension.php | 63 +++ templates/blocks/block.html.twig | 2 +- tests/ArchiveUpdateTest.php | 4 + tests/SystemScreenTest.php | 41 ++ 14 files changed, 617 insertions(+), 11 deletions(-) create mode 100644 public/assets/fonts/dm-sans-600-latin-ext.woff2 create mode 100644 public/assets/fonts/dm-sans-600-latin.woff2 create mode 100644 scripts/translate-kantarci-reisen.php diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 6ccb523..a03c3d8 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -2,6 +2,18 @@ Stand: 09.10.2026. Arbeitstitel KantSites; Produktname offen. +## ADR 112 – Das Archivupdate läuft in einem Zug durch (09.10.2026) + +Umut nach dem Update von kantarci-reisen.de auf 1.27.0: „Update durchgelaufen, direkt danach kam 500, dann bin ich zurück und hab migrieren können.“ Und: „Können wir den Updater nicht besser machen, dass das alles automatisch abläuft, einfach wie bei WordPress?“ + +**Befund.** Lokal mit dem Releasearchiv 1.26.0 und der Reisen-Datenbank vor der Migration nachgestellt: Alle Schritte bis „Dateien sind ersetzt“ laufen sauber, dann stirbt dieselbe Anfrage mit 500. Der PHP-Prozess hat den alten Code geladen, auf der Platte liegt der neue; was danach noch nachgeladen wird (Antwort-Listener, Sitzung, Vorlagen), passt nicht mehr zum Geladenen. ADR 071 hatte das gesehen und die Migrationen deshalb in eine zweite Anfrage gelegt – aber die erste Anfrage lief nach dem Tausch trotzdem durch das ganze Framework weiter. + +**Entscheidung.** Die Anfrage, die tauscht, gibt danach nur noch eine schlichte Seite mit reinem PHP aus (`SystemController::sendAndStop`: Kopfzeilen, Text, `exit`) – kein Framework-Code mehr. Die Seite ruft per `` sofort `GET /admin/system/abschliessen?token=…` auf. Das ist eine neue Anfrage mit dem neuen Code: Migrationen, Aufräumen, Wartungszustand aus, Weiterleitung zur Systemseite mit „Update auf x.y.z abgeschlossen“. Ein Klick, keine Zwischenseite zum Bedienen, kein 500er. + +**Sicherheit.** Der Einmalschlüssel (32 Hexzeichen) steht in `var/update/offen.json` neben Fassung und Zeitpunkt; der Abschluss verlangt ihn und die Administratorrolle (`hash_equals`). Er ersetzt das CSRF-Token, das die Zwischenseite nicht mehr erzeugen kann. Neu laden findet nichts mehr offen und meldet „bereits abgeschlossen“. Der bisherige Knopf „Migrationen ausführen“ bleibt als Rückweg, falls die Weiterleitung unterdrückt wird; daneben steht ein Link. + +**Folge.** Wirkt ab Updates, die von einer Fassung mit diesem Code ausgehen: von 1.27.0 auf 1.27.1 läuft noch der alte Weg (einmal 500, dann „Migrationen ausführen“), ab 1.27.1 der neue. Geprüft mit einer echten Installation aus dem Archiv, die auf ein lokal gebautes 1.27.1 aktualisiert (Downloader für die Probe auf lokale Dateien umgestellt): Zwischenseite mit 200, Abschluss 302, Fassung 1.27.1, Wartungszustand aus. + ## ADR 111 – Mehrsprachigkeit als Übersetzungsebene, nicht als zweite Seite (Dokumentschema v27, 09.10.2026) Umut: „Ich füge weitere Sprache hinzu und man wählt aus und muss dann zu jedem Feld die 2. Sprache hinzufügen. Ich möchte nicht für die 2. Sprache die komplette Seite neu bauen.“ Danach: „`/en/`-Präfix und übersetzte Slugs.“ Das Datenmodell hatte seit ADR 003 eine Spalte `locale` an jeder Seite – also das WPML-Prinzip „eine Sprache = eine eigene Seite“. Genau das wollte Umut nicht. diff --git a/docs/STATUS.md b/docs/STATUS.md index c690678..0624f7b 100644 --- a/docs/STATUS.md +++ b/docs/STATUS.md @@ -1,5 +1,21 @@ # Projektstatus +## Fassung 1.27.1: Update in einem Zug, Kantarci Reisen auf Türkisch (09.10.2026) + +**Anlass:** Umut: Update auf kantarci-reisen.de gab direkt danach einen 500er, nach „zurück“ ließ sich migrieren. „Können wir den Updater besser machen, dass alles automatisch abläuft, wie bei WordPress?“ Und: „Die DB hier komplett mit Übersetzung auf Türkisch fertig machen.“ + +**Updater (ADR 112):** Ein Klick spielt ein, die Anfrage endet nach dem Dateitausch mit einer schlichten Seite, die den Abschluss (Migrationen, Wartung aus) selbst aufruft – kein 500er, kein zweiter Knopf. Wirkt ab Updates, die von 1.27.1 ausgehen; 1.27.0 → 1.27.1 läuft noch den alten Weg. + +**Türkisch im Kern vervollständigt:** feste Vorlagentexte und Formularmeldungen komplett auf Türkisch (vorher Teile auf Englisch). Zwei Sprachwahlen auf einer Seite heißen jetzt verschieden („Sprache“ im Kopf, „Sprache wählen“ sonst) – axe meldete sonst `landmark-unique`. + +**Kantarci Reisen (nur Inhalt):** `scripts/translate-kantarci-reisen.php` schaltet Türkisch ein und übersetzt Startseite (67 Texte), Künye (`/tr/kunye`), Gizlilik (`/tr/gizlilik`), 404, Kopf, Fuß, Menü und das Anfrageformular; Anrede „siz“, Rechtstexte mit dem Hinweis, dass die deutsche Fassung gilt. Sprachwahl DE · TR im Kopf (auf dem Telefon ausgeblendet, kein Platz) und „Deutsch · Türkçe“ im Fuß. `reset-kantarci-reisen.sh` ruft das Skript am Ende auf. Ausgeführt auf `var/kantarci-reisen.db` (Sicherung `var/backups/vor-1.27.0/kantarci-reisen-vor-tr.db`). + +**Korrigiert:** Die DM-Sans-600-Dateien von heute Nachmittag waren doch nötig – `FontLibrary::missing()` verlangt je Stärke eine Datei, sonst gibt die Website ihre eigenen Schriften ganz auf. Neu geholt (`cms:fonts:fetch`) und diesmal mit `fonts.css` committet. Live auf kantarci-reisen.de waren sie nie weg. + +**Geprüft:** PHPUnit vollständig 637 grün (neu: Abschluss per Einmalschlüssel, Rolle, falscher Schlüssel, Neuladen), `npm test`, `lint:twig`, php-cs-fixer; Update-Ende-zu-Ende in einer Archivinstallation; `test:browser` auf Reisen (8104) grün; Lighthouse Produktion (`reports/lighthouse-kantarci-reisen-tr`): `/tr`, `/tr/gizlilik`, `/` überall 100, nur Leistung mobil 99 auf `/tr` und `/`. Telefonbreite ohne waagrechtes Scrollen. + +**Für den Livegang der türkischen Fassung:** erst kantarci-reisen.de auf 1.27.1 aktualisieren (sonst fehlen Teile der festen türkischen Texte), dann `var/kantarci-reisen.db` hochladen. Achtung: die hochgeladene Datei ersetzt die Datenbank dort – Einsendungen und Änderungen, die es nur live gibt, gehen dabei verloren. + ## Fassung 1.27.0: Mehrsprachigkeit und übersichtlichere Formulare (09.10.2026) **Inhalt:** Mehrsprachigkeit in drei Stufen (ADR 111, Schema v27, Migration `20261009000100`) und die neue Formularverwaltung – Einzelheiten in den drei Einträgen darunter. diff --git a/public/assets/fonts/dm-sans-600-latin-ext.woff2 b/public/assets/fonts/dm-sans-600-latin-ext.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..db39c6263056cff430c828fe7035b9ba5edaa6ee GIT binary patch literal 18228 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 literal 0 HcmV?d00001 diff --git a/public/assets/fonts/dm-sans-600-latin.woff2 b/public/assets/fonts/dm-sans-600-latin.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..01383d7e5c4340847b8fe4570ad0458e6582989d GIT binary patch literal 36932 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 literal 0 HcmV?d00001 diff --git a/public/assets/fonts/fonts.css b/public/assets/fonts/fonts.css index efccc89..8285546 100644 --- a/public/assets/fonts/fonts.css +++ b/public/assets/fonts/fonts.css @@ -15,10 +15,6 @@ /* latin */ @font-face{font-family:'Cormorant Garamond';font-style:normal;font-weight:700;font-display:swap;src:url(/assets/fonts/cormorant-garamond-700-latin.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD} /* latin-ext */ -@font-face{font-family:'DM Sans';font-style:normal;font-weight:400 700;font-display:swap;src:url(/assets/fonts/dm-sans-400-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF} -/* latin */ -@font-face{font-family:'DM Sans';font-style:normal;font-weight:400 700;font-display:swap;src:url(/assets/fonts/dm-sans-400-latin.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD} -/* latin-ext */ @font-face{font-family:'Barlow';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/barlow-400-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF} /* latin */ @font-face{font-family:'Barlow';font-style:normal;font-weight:400;font-display:swap;src:url(/assets/fonts/barlow-400-latin.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD} @@ -38,3 +34,7 @@ @font-face{font-family:'Barlow Condensed';font-style:normal;font-weight:800;font-display:swap;src:url(/assets/fonts/barlow-condensed-800-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF} /* latin */ @font-face{font-family:'Barlow Condensed';font-style:normal;font-weight:800;font-display:swap;src:url(/assets/fonts/barlow-condensed-800-latin.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD} +/* latin-ext */ +@font-face{font-family:'DM Sans';font-style:normal;font-weight:400 700;font-display:swap;src:url(/assets/fonts/dm-sans-400-latin-ext.woff2) format('woff2');unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF} +/* latin */ +@font-face{font-family:'DM Sans';font-style:normal;font-weight:400 700;font-display:swap;src:url(/assets/fonts/dm-sans-400-latin.woff2) format('woff2');unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD} diff --git a/scripts/reset-kantarci-reisen.sh b/scripts/reset-kantarci-reisen.sh index 2b7dc94..e347ea3 100644 --- a/scripts/reset-kantarci-reisen.sh +++ b/scripts/reset-kantarci-reisen.sh @@ -25,3 +25,5 @@ if [ -f "$root/var/cacert.pem" ]; then else "$php" bin/console cms:fonts:fetch fi +# Tuerkisch als zweite Sprache (ADR 111): Seiten, Kopf, Fuss, Menue, Formular, Sprachwahl. +"$php" scripts/translate-kantarci-reisen.php "$root/var/kantarci-reisen.db" diff --git a/scripts/translate-kantarci-reisen.php b/scripts/translate-kantarci-reisen.php new file mode 100644 index 0000000..0abf777 --- /dev/null +++ b/scripts/translate-kantarci-reisen.php @@ -0,0 +1,381 @@ +Bu çeviri bilgilendirme amaçlıdır; hukuken bağlayıcı olan Almanca metindir.'; + +// --------------------------------------------------------------------------------------------- +// Seiten: Adresse, Titel, Beschreibung, Suchtitel und die Texte je Block +// --------------------------------------------------------------------------------------------- + +$seiten = [ + 'home' => [ + 'title' => 'Başka bir yer için zaman.', + 'description' => 'Kalbimizde Türkiye. Gözümüz dünyada. Baden-Baden’de kişisel seyahat danışmanlığı: Türkiye tatili, dünya genelinde oteller ve Türkiye uçuşları.', + 'seoTitle' => 'Kantarci Reisen – Türkiye tatili, oteller ve uçuşlar | Baden-Baden seyahat acentesi', + 'blocks' => [ + 'hero-klein.text' => 'Kalbimizde Türkiye. Gözümüz dünyada.', + 'hero-titel.text' => 'Başka bir yer
için zaman.', + 'hero-satz.text' => 'Tende tuz. Yüzde güneş.
Ve size uygun bir seyahat.', + 'hero-knopf.text' => 'Tatilinizi planlayın', + 'hero-unten-links.text' => 'Kişisel danışmanlık.
Rahat seyahat.', + 'hero-unten-rechts.text' => 'Baden-Baden · Oos', + 'hero-foto.alt' => 'Güneşli bir plaj terasıyla turkuaz Akdeniz koyu', + 'hero-etikett.text' => 'Seyahat ilhamı', + 'hero-bildzeile.text' => 'Yeni favori yerinizBiraz daha fazla deniz.', + 'reisen-klein.text' => 'Sizi nereye çekiyor?', + 'reisen-titel.text' => 'Sizde gezme isteği var.
Bizde fikirler.', + 'reisen-satz.text' => 'Denizde bir mola, yeni bir favori yer ya da aile ziyareti. Size uygun seyahati birlikte buluyoruz.', + 'angebot-1-klein.text' => 'Uzmanlık alanımız', + 'angebot-1-titel.text' => 'Türkiye.
Tam size göre.', + 'angebot-1-satz.text' => 'Türk Rivierası’ndan Ege’ye. Misafirperverlik, sahil ve kültür arasında bir tatil keşfedin – ilk elden ipuçlarıyla.', + 'angebot-1-link.text' => 'Türkiye tatili sorun', + 'angebot-2-klein.text' => 'Dünya genelinde oteller', + 'angebot-2-titel.text' => 'Giriş yapın.
Kafanızı dinleyin.', + 'angebot-2-satz.text' => 'Küçük bir butik otel mi, geniş bir resort mu? Tatiliniz için doğru yeri bulmanıza yardımcı oluyoruz.', + 'angebot-2-link.text' => 'Otel sorun', + 'angebot-3-klein.text' => 'Türkiye uçuşları', + 'angebot-3-titel.text' => 'Bazen hedef
evdir.', + 'angebot-3-satz.text' => 'Tatil, aile ya da sadece özlem için. Size uygun uçuşu buluyoruz – Karlsruhe/Baden-Baden, Stuttgart veya Frankfurt’tan da.', + 'angebot-3-link.text' => 'Türkiye uçuşu sorun', + 'beratung-klein.text' => 'Seyahat acenteniz.
Doğrudan iletişiminiz.', + 'beratung-ort-text.text' => 'Kantarci Reisen
Ooser Hauptstraße 51
76532 Baden-Baden-Oos', + 'beratung-titel.text' => 'İyi seyahatler
bir sohbetle başlar.', + 'beratung-satz.text' => 'Sizin için neyin önemli olduğunu anlatın. Dinliyor, birlikte düşünüyor ve sizinle planlıyoruz. Kişisel, kolay ve Türkiye’ye özel bir yakınlıkla – ister plaj tatili, ister aile ziyareti, ister İstanbul’a şehir turu.', + 'beratung-knopf.text' => 'Hadi konuşalım', + 'schritt-1-titel.text' => 'İsteğinizi anlatın', + 'schritt-1-satz.text' => 'Form, e-posta ya da doğrudan Oos’taki ofisimizde. Kabaca bir plan yeterli.', + 'schritt-2-titel.text' => 'Önerileri alın', + 'schritt-2-satz.text' => 'Otelleri, uçuşları ve tarihleri karşılaştırıyor, size uygun fikirlerle dönüyoruz.', + 'schritt-3-titel.text' => 'Rahatça yola çıkın', + 'schritt-3-satz.text' => 'Siz karar verirsiniz, biz rezervasyon yaparız. Belgeler ve irtibat kişisi bizden.', + 'kontakt-klein.text' => 'Tatile ilk adım', + 'kontakt-titel.text' => 'Nereye
gidelim?', + 'kontakt-satz.text' => 'Bir istek. Kabaca bir plan. Ya da belli bir hedef. Bize anlatın – size kişisel olarak dönüş yapacağız.', + 'kontakt-whatsapp-satz.text' => 'Kısaca yazmayı mı tercih edersiniz? WhatsApp’ta size kişisel olarak cevap veriyoruz.', + 'kontakt-hinweis.text' => 'Türkiye seyahatleri · Dünya genelinde oteller · Türkiye uçuşları
0170 2945935 · info@kantarci-reisen.de', + 'kontakt-karte-klein.text' => 'Kişisel seyahat danışmanlığı', + 'kontakt-karte-titel.text' => 'Seyahat talebiniz', + 'kontakt-karte-satz.text' => 'İsteklerinizi yazın – bir sonraki seyahatiniz burada başlıyor.', + 'kontakt-karte-hinweis.text' => 'Bilgilerinizi yalnızca talebiniz için kullanıyoruz. Ayrıntılar gizlilik bildiriminde.', + 'reisesoft-etikett.text' => 'Seyahat acenteleri için', + 'reisesoft-klein.text' => 'Seyahatin diğer tarafı için', + 'reisesoft-titel.text' => 'Daha az idari iş.
Daha çok seyahat acentesi.', + 'reisesoft-satz.text' => 'reiseSoft, seyahat acenteleri için web platformumuz – kendi ofis günlüğümüzden doğdu. Rezervasyonlar, müşteriler, faturalar ve ödemeler tek yerde; böylece danışmanlığa daha çok zaman kalır.', + 'funktion-1-text.text' => 'Rezervasyonu Ctrl+V ile aktarınAmadeus’tan veya havayolu portalından kopyalayın, yapıştırın, kontrol edin – hazır.', + 'funktion-2-text.text' => 'Fatura ve e-faturaArdışık fatura numaraları, ZUGFeRD ve XRechnung, e-posta ile gönderim.', + 'funktion-3-text.text' => 'Açık kalemler göz önündeÖdemeleri kaydedin, ciro ve kârı değerlendirin – doğrudan tarayıcıda.', + 'reisesoft-plattform.text' => 'reiseSoft platformuna', + 'reisesoft-anfragen.text' => 'reiseSoft’u tanıyın', + 'reisesoft-bild.alt' => 'Açık kalemler, ciro ve son işlemlerle reiseSoft genel görünümü (örnek veriler) – platforma', + 'reisesoft-bildzeile.text' => 'Gerçek arayüz, hayali örnek veriler.', + // Sprachneutral, aber eingetragen: sonst zeigt der Editor sie als „fehlt“. + 'schritt-1-nummer.text' => '01', 'schritt-2-nummer.text' => '02', 'schritt-3-nummer.text' => '03', + 'funktion-1-nummer.text' => '01', 'funktion-2-nummer.text' => '02', 'funktion-3-nummer.text' => '03', + 'beratung-mail-text.text' => 'info@kantarci-reisen.de', + 'beratung-telefon-text.text' => '0170 2945935', + 'reisesoft-name.text' => 'reiseSoft', + 'reisesoft-fenster-adresse.text' => 'reisesoft.kantarci-reisen.de', + ], + ], + 'impressum' => [ + 'slug' => 'kunye', + 'title' => 'Künye', + 'description' => 'Kantarci Reisen, sahibi I. Kantarci, Baden-Baden – sağlayıcı bilgileri.', + 'seoTitle' => 'Künye | Kantarci Reisen', + 'blocks' => [ + 'impressum-label.text' => 'Yasal bilgiler', + 'impressum-h1.text' => 'Künye', + 'impressum-vorspann.text' => '§ 5 DDG ve § 18 (2) MStV uyarınca bilgiler. '.$hinweisRecht, + 'impressum-h2-0.text' => 'Sağlayıcı', + 'impressum-p-0.text' => 'Kantarci Reisen
Sahibi: I. Kantarci
Ooser Hauptstraße 51
76532 Baden-Baden', + 'impressum-h2-1.text' => 'İletişim', + 'impressum-p-1.text' => 'Telefon: 0170 2945935
E-posta: info@kantarci-reisen.de
WhatsApp: 0170 2945935', + 'impressum-h2-2.text' => 'İçerikten sorumlu', + 'impressum-p-2.text' => 'I. Kantarci, adres yukarıdaki gibi.', + 'impressum-h2-3.text' => 'Seyahat aracılığı', + 'impressum-p-3.text' => 'Kantarci Reisen paket turlar, otel konaklamaları ve uçuşlar gibi seyahat hizmetlerine aracılık eder. İlgili hizmetin sözleşme tarafı tur operatörü, otel veya havayolu şirketidir; rezervasyondan önce size ilettiğimiz onların genel ve taşıma koşulları geçerlidir.', + 'impressum-h2-4.text' => 'Tüketici uyuşmazlıklarının çözümü', + 'impressum-p-4.text' => 'Bir tüketici hakem kurulu önündeki uyuşmazlık çözüm sürecine katılmaya hazır ve yükümlü değiliz.', + 'impressum-h2-5.text' => 'İçerikler için sorumluluk', + 'impressum-p-5.text' => 'Hizmet sağlayıcı olarak bu sayfalardaki kendi içeriklerimizden genel yasalar çerçevesinde sorumluyuz. Seyahat bilgilerini bilgimiz dahilinde aktarıyoruz; bağlayıcı olan ilgili teklifteki bilgilerdir. Hukuka aykırılıklardan haberdar olduğumuzda ilgili içerikleri derhal kaldırırız.', + 'impressum-h2-6.text' => 'Bağlantılar için sorumluluk', + 'impressum-p-6.text' => 'Bu web sitesi reisesoft.kantarci-reisen.de adresindeki reiseSoft platformuna yönlendirir. Yabancı sayfaların içeriklerinden ilgili sağlayıcı sorumludur.', + 'impressum-h2-7.text' => 'Telif hakkı', + 'impressum-p-7.text' => 'Bu sayfalarda tarafımızdan oluşturulan içerik ve eserler Alman telif hakkı yasasına tabidir. Telif hakkı sınırları dışında çoğaltma, işleme, yayma ve her türlü kullanım yazılı izne bağlıdır.', + 'impressum-h2-8.text' => 'Görsel kaynakları', + 'impressum-p-8.text' => 'Logo: Kantarci Reisen. Ana sayfadaki seyahat fotoğrafı: yapay zekâ ile üretilmiştir. Ekran görüntüsü: hayali örnek verilerle reiseSoft.', + ], + ], + 'datenschutz' => [ + 'slug' => 'gizlilik', + 'title' => 'Gizlilik', + 'description' => 'Kantarci Reisen web sitesini ziyaret ederken ve seyahat talebinde hangi verilerin işlendiği.', + 'seoTitle' => 'Gizlilik | Kantarci Reisen', + 'blocks' => [ + 'datenschutz-label.text' => 'Verileriniz', + 'datenschutz-h1.text' => 'Gizlilik', + 'datenschutz-vorspann.text' => 'Bu web sitesini ziyaret ederken ve bir talepte bulunduğunuzda hangi verilerin işlendiğini ve hangi haklara sahip olduğunuzu burada öğrenebilirsiniz. '.$hinweisRecht, + 'datenschutz-h2-0.text' => '1. Sorumlu', + 'datenschutz-p-0.text' => 'Kantarci Reisen, sahibi I. Kantarci, Ooser Hauptstraße 51, 76532 Baden-Baden. Telefon 0170 2945935, e-posta info@kantarci-reisen.de.', + 'datenschutz-h2-1.text' => '2. Bu web sitesinin ziyareti', + 'datenschutz-p-1.text' => 'Sayfa açıldığında barındırma hizmeti sağlayıcımız teknik olarak gerekli bağlantı verilerini işler: IP adresi, zaman, istenen adres, tarayıcı bilgileri ve HTTP durumu. Bu, güvenli sunum ve hata ayıklama içindir. Hukuki dayanak, kesintisiz işletimdeki meşru menfaatimizdir, GVKT (DSGVO) md. 6 f. 1 b. f.

Barındırma hizmeti sağlayıcısıyla GVKT md. 28 uyarınca bir veri işleme sözleşmesi bulunmaktadır. Sunucu kayıtları, bir güvenlik olayının aydınlatılması için daha uzun süre gerekmedikçe en geç yedi gün sonra silinir veya anonimleştirilir.', + 'datenschutz-h2-2.text' => '3. Çerezler ve harici hizmetler', + 'datenschutz-p-2.text' => 'Bu web sitesi analiz veya reklam çerezi kullanmaz. Resimler, yazı tipleri ve tasarım kendi sunucumuzdan gelir; üçüncü taraflardan harita, video veya yazı tipi yüklenmez. Bu nedenle bir onay penceresi de yoktur.', + 'datenschutz-h2-3.text' => '4. Seyahat talebi', + 'datenschutz-p-3.text' => 'Bize form aracılığıyla yazdığınızda, talebinizi yanıtlamak ve uygun teklifler hazırlamak için adınızı, e-posta adresinizi, isteğe bağlı olarak telefon numaranızı, seyahat dönemini, kişi sayısını ve isteklerinizi işleriz (GVKT md. 6 f. 1 b. b). Talep sistemimizde kaydedilir ve e-posta ile bize iletilir. Talep sonuçlandığında ve yasal saklama yükümlülüğü bulunmadığında silinir.', + 'datenschutz-h2-4.text' => '5. Rezervasyonlar', + 'datenschutz-p-4.text' => 'Bir rezervasyon yapılırsa, bunun için gerekli bilgileri (örneğin kimlikteki yolcu adları ve seyahat tarihleri) tur operatörüne, otele veya havayolu şirketine iletiriz (GVKT md. 6 f. 1 b. b). Fatura belgelerini ticaret ve vergi hukukunun gerektirdiği süre boyunca saklarız.', + 'datenschutz-h2-5.text' => '6. E-posta veya telefonla iletişim', + 'datenschutz-p-5.text' => 'Bize yazdığınızda veya bizi aradığınızda, talebinizi işlemek için bilgilerinizi işleriz (GVKT md. 6 f. 1 b. b veya f).', + 'datenschutz-h2-6.text' => '7. WhatsApp', + 'datenschutz-p-6.text' => 'WhatsApp düğmeleri basit bağlantılardır. Yalnızca birini kullandığınızda, WhatsApp Ireland Limited (Meta) tarafından sunulan WhatsApp açılır. WhatsApp bu sırada telefon numaranızı, mesajlarınızı ve kullanım verilerinizi kendi gizlilik bildirimine göre, AB dışındaki ülkelerde de işler. Mesajınızı yalnızca talebinizi yanıtlamak için kullanırız (GVKT md. 6 f. 1 b. b). Bunu istemiyorsanız bize form, e-posta veya telefonla da ulaşabilirsiniz.', + 'datenschutz-h2-7.text' => '8. reiseSoft', + 'datenschutz-p-7.text' => 'reiseSoft platformuna giden bağlantı, reisesoft.kantarci-reisen.de adresindeki ayrı bir uygulamaya yönlendirir. Veriler ancak bağlantıyı takip ettiğinizde orada işlenir; kayıtlı seyahat acenteleri için platformun bildirimleri geçerlidir.', + 'datenschutz-h2-8.text' => '9. Haklarınız', + 'datenschutz-p-8.text' => 'Yasal koşullar çerçevesinde bilgi alma, düzeltme, silme, işlemenin kısıtlanması ve veri taşınabilirliği haklarına sahipsiniz. GVKT md. 6 f. 1 b. f’ye dayanan bir işlemeye itiraz edebilirsiniz.', + 'datenschutz-h2-9.text' => '10. Şikâyet hakkı', + 'datenschutz-p-9.text' => 'Bir veri koruma denetim makamına şikâyette bulunabilirsiniz; bizim için yetkili makam Baden-Württemberg Veri Koruma ve Bilgi Edinme Özgürlüğü Görevlisi’dir (Landesbeauftragter für den Datenschutz und die Informationsfreiheit Baden-Württemberg).', + 'datenschutz-p-10.text' => 'Gizlilikle ilgili sorularınızı info@kantarci-reisen.de adresinden memnuniyetle yanıtlarız.', + ], + ], + '404' => [ + 'title' => 'Sayfa bulunamadı', + 'description' => 'Aradığınız sayfa mevcut değil veya taşınmış.', + 'blocks' => [ + 'fehler-label.text' => 'Hata 404', + 'fehler-titel.text' => 'Maalesef burada
devam edilemiyor.', + 'fehler-text.text' => 'Bu sayfa mevcut değil veya taşınmış. Ana sayfadan bir sonraki hedefinizi kolayca bulabilirsiniz.', + 'fehler-knopf.text' => 'Ana sayfaya', + ], + ], +]; + +// --------------------------------------------------------------------------------------------- +// Kopf, Fuß, Menü, Formular +// --------------------------------------------------------------------------------------------- + +$bereiche = [ + 'header' => ['kopf-logo.alt' => 'Kantarci Reisen – ana sayfaya'], + 'footer' => [ + 'fuss-logo.alt' => 'Kantarci Reisen', + 'fuss-copyright.text' => '© 2026 Kantarci Reisen', + 'fuss-spruch.text' => 'Kalbimizde Türkiye. Gözümüz dünyada.', + 'fuss-reisen-titel.text' => 'Seyahatler', + 'fuss-reisen-liste.text' => 'Türkiye tatili
Dünya genelinde oteller
Türkiye uçuşları
Seyahat talebi', + 'fuss-business-titel.text' => 'Seyahat acenteleri için', + 'fuss-business-liste.text' => 'reiseSoft’u tanıyın
reiseSoft platformuna', + 'fuss-kontakt-titel.text' => 'İletişim', + 'fuss-kontakt-text.text' => 'Kantarci Reisen
Ooser Hauptstraße 51
76532 Baden-Baden
0170 2945935
info@kantarci-reisen.de
WhatsApp', + 'fuss-rechtliches.label' => 'Yasal bilgiler', + 'fuss-agentur.text' => 'Web sitesi: Kant-IT Solutions', + ], +]; + +$menue = [ + 'Reisen' => 'Seyahatler', + 'Über uns' => 'Hakkımızda', + 'reiseSoft' => 'reiseSoft', + 'Kontakt' => 'İletişim', + 'Reise anfragen' => 'Seyahat talebi', +]; + +$formular = [ + 'buttonLabel' => 'Talebi gönder', + 'success' => 'Teşekkürler! Talebiniz bize ulaştı. Size kişisel olarak dönüş yapacağız – genellikle bir iş günü içinde.', + 'fields' => [ + 'name' => ['label' => 'Adınız', 'help' => 'Ad ve soyad'], + 'email' => ['label' => 'E-posta adresiniz'], + 'telefon' => ['label' => 'Telefon', 'help' => 'İsteğe bağlı – sizi aramamızı tercih ederseniz'], + 'anliegen' => ['label' => 'Konu', 'options' => [ + 'Türkei-Urlaub' => 'Türkiye tatili', + 'Hotels weltweit' => 'Dünya genelinde oteller', + 'Flüge in die Türkei' => 'Türkiye uçuşları', + 'reiseSoft für mein Reisebüro' => 'Seyahat acentem için reiseSoft', + 'Allgemeine Anfrage' => 'Genel talep', + ]], + 'zeitraum' => ['label' => 'Seyahat dönemi', 'help' => 'ör. Pentekost tatili veya Ağustos’ta 2 hafta'], + 'personen' => ['label' => 'Kişi sayısı'], + 'wuensche' => ['label' => 'İstekleriniz', 'help' => 'Varış noktası, kalkış havalimanı, bütçe, sizin için önemli olanlar …'], + 'datenschutz' => ['label' => 'Bilgilerimin gizlilik bildirimine uygun olarak işlenmesini kabul ediyorum.'], + ], +]; + +// --------------------------------------------------------------------------------------------- +// Ausführen +// --------------------------------------------------------------------------------------------- + +$languages->save([SPRACHE]); +echo "Sprache eingeschaltet: Türkisch\n"; + +/** Legt die Übersetzung auf das Dokument; meldet Schlüssel, die es im Dokument nicht gibt. */ +$uebersetzen = static function (array $document, array $daten, string $ort): array { + $texte = []; + $vorhanden = []; + $walk = static function (array $blocks) use (&$walk, &$vorhanden): void { + foreach ($blocks as $block) { + foreach (Translations::FIELDS[$block['type'] ?? ''] ?? [] as $prop) { + $vorhanden[$block['id'].'.'.$prop] = $block['props'][$prop] ?? ''; + } + $walk($block['children'] ?? []); + } + }; + $walk($document['blocks']); + $translation = []; + $source = []; + foreach ($daten['blocks'] ?? $daten as $schluessel => $text) { + if (!array_key_exists($schluessel, $vorhanden)) { + throw new RuntimeException(sprintf('%s: „%s“ gibt es im Dokument nicht.', $ort, $schluessel)); + } + [$id, $prop] = explode('.', $schluessel, 2); + $translation['blocks'][$id][$prop] = $text; + $source[$schluessel] = Translations::fingerprint((string) $vorhanden[$schluessel]); + } + foreach (['title', 'description'] as $feld) { + if (isset($daten[$feld])) { + $translation[$feld] = $daten[$feld]; + $source[$feld] = Translations::fingerprint((string) $document[$feld]); + } + } + foreach (['slug', 'seoTitle'] as $feld) { + if (isset($daten[$feld])) { + $translation[$feld] = $daten[$feld]; + } + } + $translation['source'] = $source; + $document['schemaVersion'] = max((int) $document['schemaVersion'], Translations::SCHEMA); + $document['translations'] = [SPRACHE => $translation]; + + return $document; +}; + +foreach ($seiten as $pfad => $daten) { + $zeile = $db->fetchAssociative('SELECT id, version FROM page WHERE path = ? AND deleted_at IS NULL', [$pfad]); + if (false === $zeile) { + throw new RuntimeException("Seite /{$pfad} fehlt."); + } + $entwurf = $pages->findForEditor($zeile['id']); + $dokument = $uebersetzen($entwurf['document'], $daten, '/'.$pfad); + $revision = $pages->saveDraft($zeile['id'], $dokument, $entwurf['version']); + $pages->publish($zeile['id'], $revision, $entwurf['version'] + 1); + $zaehlung = Translations::progress($dokument, SPRACHE); + printf("Seite /%s: %d von %d Texten übersetzt, veröffentlicht\n", $pfad, $zaehlung['done'], $zaehlung['total']); +} + +foreach ($bereiche as $art => $texte) { + $bereich = $partials->findByKind($art); + $dokument = $uebersetzen($bereich['document'], $texte, $art); + if ('header' === $art) { + // Die Sprachwahl rechts neben dem Menü, als Kürzel. Auf dem Telefon bleibt sie neben dem + // Menü-Knopf stehen; das Menü selbst rückt mit margin-left:auto nach rechts. + $zeile = &$dokument['blocks'][0]['children'][0]; + $zeile['children'] = array_values(array_filter($zeile['children'], static fn (array $b): bool => 'kopf-sprache' !== $b['id'])); + foreach ($zeile['children'] as &$kind) { + if ('kopf-menue' === $kind['id'] && !str_contains($kind['css'] ?? '', 'margin-left:auto')) { + $kind['css'] = "&{margin-left:auto}\n".($kind['css'] ?? ''); + } + } + unset($kind); + $zeile['children'][] = [ + 'id' => 'kopf-sprache', 'type' => 'language', 'props' => ['display' => 'code'], + // Auf dem Telefon ist neben Logo, Anfrageknopf und Menü kein Platz - dort steht die + // Sprachwahl im Fuß. + 'layout' => ['visibility' => 'hide-mobile'], + 'style' => [ + 'desktop' => ['fontSize' => '14px', 'fontWeight' => '600', 'color' => '@tinte', 'margin' => ['left' => '20px']], + 'mobile' => ['margin' => ['left' => '0px']], + ], + 'css' => "& a{border-radius:4px;letter-spacing:.04em}\n& a[aria-current]{box-shadow:inset 0 -2px 0 #ff753d}\n& a:hover{text-decoration:none;background:#f1f5f5}", + ]; + unset($zeile); + } + if ('footer' === $art) { + // Unten neben der Rechtszeile, auf jedem Gerät - auf dem Telefon der Weg zur Sprache. + $sprache = [ + 'id' => 'fuss-sprache', 'type' => 'language', 'props' => ['display' => 'name'], + 'style' => ['desktop' => ['fontSize' => '13px']], + 'css' => "& a{padding:2px 6px;color:inherit} +& a[aria-current]{box-shadow:inset 0 -2px 0 #ff753d}", + ]; + $einsetzen = static function (array $blocks) use (&$einsetzen, $sprache): array { + foreach ($blocks as $i => $block) { + $kinder = array_values(array_filter($block['children'] ?? [], static fn (array $b): bool => 'fuss-sprache' !== $b['id'])); + if ('fuss-links' === $block['id']) { + array_unshift($kinder, $sprache); + } + $blocks[$i]['children'] = $einsetzen($kinder); + } + + return $blocks; + }; + $dokument['blocks'] = $einsetzen($dokument['blocks']); + } + $partials->save($bereich['id'], $dokument, $bereich['version']); + $zaehlung = Translations::progress($dokument, SPRACHE); + printf("Bereich %s: %d von %d Texten übersetzt\n", $art, $zaehlung['done'], $zaehlung['total']); +} + +$alle = $menus->all(); +$kopfmenue = $alle['header']; +$beschriften = static function (array $items) use (&$beschriften, $menue): array { + foreach ($items as $i => $item) { + if (isset($menue[$item['label']])) { + $items[$i]['labels'] = [SPRACHE => $menue[$item['label']]]; + } + $items[$i]['children'] = $beschriften($item['children'] ?? []); + } + + return $items; +}; +$menus->save('header', $kopfmenue['name'], $beschriften($kopfmenue['items']), $kopfmenue['version']); +echo "Menü: ".count($menue)." Beschriftungen\n"; + +$form = $forms->findBySlug('reiseanfrage'); +$einstellungen = $form['settings']; +$einstellungen['translations'] = [SPRACHE => $formular]; +$forms->save('reiseanfrage', $form['name'], array_map(static fn (array $f): array => [...$f, 'images' => $f['images'] ?: '', 'min' => $f['min'] ?? '', 'max' => $f['max'] ?? ''], $form['fields']), $einstellungen, $form['version']); +echo "Formular reiseanfrage übersetzt\n"; + +echo "Fertig: /tr, /tr/kunye, /tr/gizlilik\n"; diff --git a/src/Content/FormRepository.php b/src/Content/FormRepository.php index f75e009..db5e476 100644 --- a/src/Content/FormRepository.php +++ b/src/Content/FormRepository.php @@ -439,6 +439,20 @@ final class FormRepository 'Bitte gib mindestens %s an.' => 'Please enter at least %s.', 'Bitte gib einen Wert zwischen %s und %s an.' => 'Please enter a value between %s and %s.', ], + 'tr' => [ + 'Bitte wähle nur aus den angebotenen Optionen.' => 'Lütfen yalnızca sunulan seçeneklerden seçin.', + 'Bitte wähle mindestens eine Option.' => 'Lütfen en az bir seçenek seçin.', + 'Bitte kürze die Eingabe auf höchstens %d Zeichen.' => 'Lütfen girişinizi en fazla %d karaktere kısaltın.', + 'Ohne diese Einwilligung können wir die Anfrage nicht bearbeiten.' => 'Bu onay olmadan talebinizi işleme alamayız.', + 'Bitte wähle eine der angebotenen Optionen.' => 'Lütfen sunulan seçeneklerden birini seçin.', + 'Dieses Feld wird benötigt.' => 'Bu alan zorunludur.', + 'Bitte gib eine gültige E-Mail-Adresse an.' => 'Lütfen geçerli bir e-posta adresi girin.', + 'Bitte gib eine gültige Telefonnummer an.' => 'Lütfen geçerli bir telefon numarası girin.', + 'Bitte gib eine Zahl an.' => 'Lütfen bir sayı girin.', + 'Bitte gib höchstens %s an.' => 'Lütfen en fazla %s girin.', + 'Bitte gib mindestens %s an.' => 'Lütfen en az %s girin.', + 'Bitte gib einen Wert zwischen %s und %s an.' => 'Lütfen %s ile %s arasında bir değer girin.', + ], ]; private static function message(?string $locale, string $text, mixed ...$args): string diff --git a/src/Controller/SystemController.php b/src/Controller/SystemController.php index c9cb7f6..102848a 100644 --- a/src/Controller/SystemController.php +++ b/src/Controller/SystemController.php @@ -174,13 +174,14 @@ final class SystemController extends AbstractController return $this->redirectToRoute('admin_system'); } + // Alles, was nach dem Dateitausch noch gebraucht wird, steht vorher fest: Danach darf diese + // Anfrage keinen Code mehr nachladen (siehe sendAndStop). + $abschluss = '/'.trim($this->adminPath, '/').'/system/abschliessen?token='; $ergebnis = $this->archive->run($request->request->getString('target') ?: null); - $request->getSession()->set('update_steps', $ergebnis['steps']); if ($ergebnis['migrate'] ?? false) { - $this->addFlash('success', 'Die Dateien sind ersetzt. Jetzt noch die Migrationen - der Knopf steht oben.'); - - return $this->redirectToRoute('admin_system'); + $this->sendAndStop((string) $ergebnis['fassung'], $abschluss.$ergebnis['token'], $ergebnis['steps']); } + $request->getSession()->set('update_steps', $ergebnis['steps']); $this->addFlash($ergebnis['ok'] ? 'success' : 'error', $ergebnis['ok'] ? 'Es war nichts einzuspielen.' : 'Das Update ist nicht durchgelaufen. Die Schritte stehen unten.'); @@ -188,6 +189,73 @@ final class SystemController extends AbstractController return $this->redirectToRoute('admin_system'); } + /** + * Der zweite Schritt des Archivupdates, aufgerufen von der Zwischenseite selbst (ADR 112). + * + * Läuft in einer eigenen Anfrage und damit mit dem Code der neuen Fassung: Migrationen, + * Aufräumen, Wartungszustand aus. Der Einmalschlüssel aus var/update/offen.json ersetzt das + * CSRF-Token, das die Zwischenseite nicht mehr erzeugen kann; ohne Administratorrolle geht es + * ohnehin nicht. Ein zweiter Aufruf (Neuladen) findet nichts mehr offen und meldet das. + */ + #[Route('/%kant.admin_path%/system/abschliessen', name: 'admin_system_finish', methods: ['GET'])] + public function finishArchive(Request $request): Response + { + $this->denyAccessUnlessGranted('ROLE_ADMIN'); + $offen = $this->archive->pending(); + if (null === $offen) { + $this->addFlash('success', 'Das Update ist bereits abgeschlossen.'); + + return $this->redirectToRoute('admin_system'); + } + if (!is_string($offen['token'] ?? null) || !hash_equals($offen['token'], $request->query->getString('token'))) { + return $this->back('Dieser Link gehört zu keinem offenen Update. Schließe es oben mit „Migrationen ausführen“ ab.'); + } + @set_time_limit(0); + $ergebnis = $this->archive->finish($this->installation); + $request->getSession()->set('update_steps', $ergebnis['steps']); + $this->addFlash($ergebnis['ok'] ? 'success' : 'error', $ergebnis['ok'] + ? sprintf('Update auf %s abgeschlossen. Die Website ist wieder erreichbar.', $offen['fassung']) + : 'Die Migrationen sind fehlgeschlagen. Die Website bleibt im Wartungszustand.'); + + return $this->redirectToRoute('admin_system'); + } + + /** + * Beendet die Anfrage, die gerade die Dateien getauscht hat, mit einer schlichten Seite. + * + * Dieser Prozess hat den alten Code im Speicher, auf der Platte liegt der neue. Alles, was + * jetzt noch nachgeladen würde - Antwort-Listener, Sitzung, Vorlagen -, käme aus der neuen + * Fassung und passte nicht zu dem, was schon geladen ist. Genau das endete vorher mit einem + * 500er, obwohl das Update gelungen war. Deshalb hier nur noch PHP selbst: Kopfzeilen, Text, + * Ende. Die Seite ruft den Abschluss sofort selbst auf, ein Link steht für den Fall daneben, + * dass der Browser das Weiterleiten unterdrückt. + * + * @param list $schritte + */ + private function sendAndStop(string $fassung, string $weiter, array $schritte): never + { + while (ob_get_level() > 0) { + ob_end_clean(); + } + $e = static fn (string $text): string => htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); + $liste = ''; + foreach ($schritte as $schritt) { + $liste .= '

  • '.($schritt['ok'] ? '✓ ' : '! ').$e($schritt['step']).'
  • '; + } + http_response_code(200); + header('Content-Type: text/html; charset=UTF-8'); + header('Cache-Control: no-store'); + header('X-Robots-Tag: noindex'); + header("Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; frame-ancestors 'none'; base-uri 'none'"); + echo '' + .'Update wird abgeschlossen' + .'' + .'

    Update auf '.$e($fassung).' wird abgeschlossen …

    ' + .'

    Die neuen Dateien sind da. Jetzt laufen die Migrationen, danach geht es von selbst zur Systemseite.

    ' + .'
      '.$liste.'

    Weiter, falls nichts passiert

    '; + exit; + } + #[Route('/%kant.admin_path%/system/update', name: 'admin_system_update', methods: ['POST'])] public function update(Request $request): Response { diff --git a/src/Ops/ArchiveUpdate.php b/src/Ops/ArchiveUpdate.php index f0bd126..dc26bd3 100644 --- a/src/Ops/ArchiveUpdate.php +++ b/src/Ops/ArchiveUpdate.php @@ -256,23 +256,28 @@ final class ArchiveUpdate // Speicher - Klassen, die PHP schon geladen hat, laedt es nicht neu. Die Migrationen // gehoeren deshalb in eine eigene Anfrage, die mit der neuen Fassung startet. Der // Wartungszustand bleibt so lange an. + // Ein Einmalschlüssel für den Abschluss: Die Seite, die diese Anfrage noch ausgibt, ruft + // damit den zweiten Schritt selbst auf - ohne Knopf, und ohne dass eine fremde Seite ihn + // auslösen könnte (dazu braucht es zusätzlich eine Anmeldung als Administrator). + $token = bin2hex(random_bytes(16)); @file_put_contents($this->root.'/'.self::MARKE, json_encode([ 'fassung' => $ziel, 'seit' => gmdate('c'), 'alt' => basename($arbeit), + 'token' => $token, ], JSON_UNESCAPED_SLASHES)); $melden($schritte, 'Dateien sind ersetzt', true, sprintf( 'Fassung %s liegt bereit. Jetzt fehlen noch die Migrationen - sie laufen im nächsten Schritt mit dem neuen Code.', $ziel, )); - return ['ok' => true, 'steps' => $schritte, 'migrate' => true]; + return ['ok' => true, 'steps' => $schritte, 'migrate' => true, 'token' => $token, 'fassung' => $ziel]; } /** * Steht ein zweiter Schritt aus? Gibt Fassung und Zeitpunkt zurück, sonst null. * - * @return array{fassung: string, seit: string, alt: string}|null + * @return array{fassung: string, seit: string, alt: string, token?: string}|null */ public function pending(): ?array { diff --git a/src/Twig/LanguageExtension.php b/src/Twig/LanguageExtension.php index d4c2331..acfe0cb 100644 --- a/src/Twig/LanguageExtension.php +++ b/src/Twig/LanguageExtension.php @@ -49,6 +49,7 @@ final class LanguageExtension extends AbstractExtension '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.', @@ -105,6 +106,7 @@ final class LanguageExtension extends AbstractExtension 'Alle akzeptieren' => 'Tout accepter', 'Nur notwendige' => 'Uniquement nécessaires', 'Sprache' => 'Langue', + 'Sprache wählen' => 'Choisir la langue', ], 'tr' => [ 'Zum Inhalt springen' => 'İçeriğe geç', @@ -118,6 +120,67 @@ final class LanguageExtension extends AbstractExtension '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', ], ]; diff --git a/templates/blocks/block.html.twig b/templates/blocks/block.html.twig index 6a5676b..689ddc7 100644 --- a/templates/blocks/block.html.twig +++ b/templates/blocks/block.html.twig @@ -305,7 +305,7 @@ {% set sprachen = app.request.attributes.get('kant_alternates')|default([]) %} {% if editing|default(false) and sprachen|length < 2 %}{% set sprachen = [{locale: 'de', native: 'Deutsch', href: '#', current: true}, {locale: 'en', native: 'English', href: '#', current: false}] %}{% endif %} {% if sprachen|length > 1 %} -