Translate pages, areas, menus, forms and posts field by field instead of rebuilding them
A site can have further languages (Settings → Languages). A page is built once; each language adds only its texts, keyed by the stable block IDs, plus its own address under /en/… (ADR 111, document schema v27, migration 20261009000100 for page_translation). - Editor: DE | EN switch, a list of all texts with original, state (missing, translated, original changed) and filter; structure stays with the main language. Undo, drafts, publishing and revisions cover all languages at once. - Website: /en and translated slugs, lang, canonical, hreflang with x-default, og:locale, sitemap; links to translated pages follow the language; a language switch block. - Header, footer and linked sections translate the same way; menu entries carry labels per language; fixed template texts come from a small dictionary. - Forms: labels, help, options, button and success message per language; messages and the step script speak the language, the submission and the email stay in the main language. - Posts, archives, category names, consent text and the 404 page follow the language. - Single-language sites see none of it and never touch the new table. The forms administration becomes an overview with one row per form and its own edit page; fields are one line each with details collapsed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c7705b7083
commit
e15b2517b1
51 files changed
+3045
-219
No files matched your search
@@ -4,8 +4,10 @@ namespace App\Controller;
|
||||
|
||||
use App\Content\Consent;
|
||||
use App\Content\DemoContent;
|
||||
use App\Content\Languages;
|
||||
use App\Content\NotFoundPage;
|
||||
use App\Content\PageRepository;
|
||||
use App\Content\PostRepository;
|
||||
use App\Content\RedirectRepository;
|
||||
use App\Content\SiteSettings;
|
||||
use App\Content\SocialProfiles;
|
||||
@@ -19,7 +21,7 @@ use Symfony\Component\Routing\Attribute\Route;
|
||||
|
||||
final class SettingsController extends AbstractController
|
||||
{
|
||||
public function __construct(private SiteSettings $settings, private RedirectRepository $redirects, private PageRepository $pages, private Theme $theme, private NotFoundPage $notFoundPage)
|
||||
public function __construct(private SiteSettings $settings, private RedirectRepository $redirects, private PageRepository $pages, private Theme $theme, private NotFoundPage $notFoundPage, private Languages $languages, private PostRepository $posts)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -67,9 +69,52 @@ final class SettingsController extends AbstractController
|
||||
'socialNetworks' => SocialProfiles::NETWORKS,
|
||||
'weekdays' => SiteSettings::DAYS,
|
||||
'socialPlaceholders' => SocialProfiles::PLACEHOLDERS,
|
||||
'mainLanguage' => $this->languages->describe($this->languages->main()),
|
||||
'extraLanguages' => $this->languages->extra(),
|
||||
'terms' => [] === $this->languages->extra() ? [] : $this->posts->allTerms(),
|
||||
'languageCatalog' => array_map(fn (string $code): array => $this->languages->describe($code), array_values(array_diff(array_keys(Languages::CATALOG), [$this->languages->main()]))),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Die weiteren Sprachen (ADR 111). Eine Sprache dazunehmen ändert an der Website nichts,
|
||||
* solange keine Seite übersetzt und veröffentlicht ist; eine Sprache wegnehmen nimmt ihre
|
||||
* Seiten vom Netz, lässt die Übersetzungen aber in den Seiten liegen.
|
||||
*/
|
||||
/** Kategorien, Schlagwörter und Einwilligungshinweis in den weiteren Sprachen (ADR 111). */
|
||||
#[Route('/%kant.admin_path%/settings/sprachen/texte', name: 'admin_language_texts_save', methods: ['POST'])]
|
||||
public function saveLanguageTexts(Request $request): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted('ROLE_ADMIN');
|
||||
if (!$this->isCsrfTokenValid('settings', $request->request->getString('_csrf_token'))) {
|
||||
return $this->back('Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.');
|
||||
}
|
||||
$all = $request->request->all();
|
||||
$this->settings->saveLanguageTexts(is_array($all['termNames'] ?? null) ? $all['termNames'] : [], is_array($all['consentTexts'] ?? null) ? $all['consentTexts'] : []);
|
||||
$this->addFlash('success', 'Die Übersetzungen wurden gespeichert.');
|
||||
|
||||
return $this->redirectToRoute('admin_settings', ['_fragment' => 'sprachen']);
|
||||
}
|
||||
|
||||
#[Route('/%kant.admin_path%/settings/sprachen', name: 'admin_languages_save', methods: ['POST'])]
|
||||
public function saveLanguages(Request $request): Response
|
||||
{
|
||||
$this->denyAccessUnlessGranted('ROLE_ADMIN');
|
||||
if (!$this->isCsrfTokenValid('settings', $request->request->getString('_csrf_token'))) {
|
||||
return $this->back('Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.');
|
||||
}
|
||||
try {
|
||||
$this->languages->save(array_values(array_map('strval', (array) ($request->request->all()['languages'] ?? []))));
|
||||
} catch (InvalidPage $e) {
|
||||
$this->addFlash('error', $e->getMessage());
|
||||
|
||||
return $this->redirectToRoute('admin_settings', ['_fragment' => 'sprachen']);
|
||||
}
|
||||
$this->addFlash('success', [] === $this->languages->extra() ? 'Die Website ist einsprachig.' : 'Die Sprachen wurden gespeichert. Übersetzt wird im Editor jeder Seite über den Sprachumschalter oben.');
|
||||
|
||||
return $this->redirectToRoute('admin_settings', ['_fragment' => 'sprachen']);
|
||||
}
|
||||
|
||||
#[Route('/%kant.admin_path%/settings/company', name: 'admin_settings_save', methods: ['POST'])]
|
||||
public function save(Request $request): Response
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user