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>
54 lines
7.7 KiB
Twig
54 lines
7.7 KiB
Twig
<li class="menu-entry{{ row.level == 2 ? ' menu-child' : '' }}" data-level="{{ row.level }}">
|
|
<input type="hidden" data-key="position" name="items[{{ n }}][position]" value="{{ n }}">
|
|
<input type="hidden" data-key="level" name="items[{{ n }}][level]" value="{{ row.level }}">
|
|
<input type="hidden" data-key="type" name="items[{{ n }}][type]" value="{{ row.type }}">
|
|
{# Der Ziehknopf steht neben der Zusammenfassung, nicht in ihr: ein Knopf in einem Knopf ist
|
|
für Screenreader und Tastatur nicht bedienbar (axe: nested-interactive). #}
|
|
<button class="menu-drag-handle" type="button" data-menu-drag aria-label="{{ row.label }} verschieben" title="Ziehen · Pfeiltasten ändern Reihenfolge und Ebene"><svg viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" fill="currentColor"><circle cx="8" cy="5" r="1.6"/><circle cx="16" cy="5" r="1.6"/><circle cx="8" cy="12" r="1.6"/><circle cx="16" cy="12" r="1.6"/><circle cx="8" cy="19" r="1.6"/><circle cx="16" cy="19" r="1.6"/></svg></button>
|
|
<details>
|
|
<summary><strong class="menu-entry-title">{{ row.label }}</strong><span class="menu-entry-kind">{{ row.type == 'page' ? 'Seitenlink' : 'Eigener Link' }}</span><svg class="menu-chevron" viewBox="0 0 24 24" width="20" height="20" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2"><path d="m6 9 6 6 6-6"/></svg></summary>
|
|
<div class="menu-entry-fields">
|
|
<label for="menu-label-{{ n }}">Beschriftung</label><input id="menu-label-{{ n }}" data-key="label" name="items[{{ n }}][label]" maxlength="80" required value="{{ row.label }}">
|
|
{% for sprache in languages|default([]) %}
|
|
<label for="menu-label_{{ sprache.code }}-{{ n }}">Beschriftung {{ sprache.name }}</label><input id="menu-label_{{ sprache.code }}-{{ n }}" data-key="label_{{ sprache.code }}" name="items[{{ n }}][label_{{ sprache.code }}]" maxlength="80" lang="{{ sprache.code }}"{{ sprache.rtl ? ' dir="rtl"' : '' }} value="{{ row.labels[sprache.code]|default('') }}" placeholder="Leer: wie „{{ row.label }}“">
|
|
{% endfor %}
|
|
{% if row.type == 'page' %}
|
|
{% include 'admin/content-picker.html.twig' with {id: 'menu-target-' ~ n, name: 'items[' ~ n ~ '][pageId]', label: 'Verknüpfter Inhalt', selected: row.page} %}
|
|
<label for="menu-anchor-{{ n }}">Sprungziel (optional)</label><input id="menu-anchor-{{ n }}" data-key="anchor" name="items[{{ n }}][anchor]" maxlength="65" value="{{ row.anchor }}" placeholder="kontakt">
|
|
{% else %}
|
|
<label for="menu-url-{{ n }}">Adresse</label><div class="link-field" data-link-field><input id="menu-url-{{ n }}" data-key="url" data-link-input name="items[{{ n }}][url]" maxlength="2048" required value="{{ row.url }}" placeholder="https://… oder /kontakt"></div>
|
|
{% endif %}
|
|
<label class="check-label" for="menu-newTab-{{ n }}"><input id="menu-newTab-{{ n }}" type="checkbox" data-key="newTab" name="items[{{ n }}][newTab]" value="1"{{ row.newTab|default(false) ? ' checked' : '' }}> In neuem Tab öffnen</label>
|
|
<div class="menu-icon-fields"><div><label for="menu-icon-{{ n }}">Icon</label><select id="menu-icon-{{ n }}" data-key="icon" name="items[{{ n }}][icon]"><option value="">Kein Icon</option>{% for icon in menuIcons %}<option value="{{ icon }}"{{ row.icon|default('') == icon ? ' selected' : '' }}>{{ icon }}</option>{% endfor %}</select></div><div><label for="menu-iconPosition-{{ n }}">Icon-Position</label><select id="menu-iconPosition-{{ n }}" data-key="iconPosition" name="items[{{ n }}][iconPosition]"><option value="before"{{ row.iconPosition|default('before') == 'before' ? ' selected' : '' }}>Vor dem Text</option><option value="after"{{ row.iconPosition|default('before') == 'after' ? ' selected' : '' }}>Nach dem Text</option></select></div></div>
|
|
{# Als Button (ADR 110). Ohne Skript sind es gewöhnliche Felder; mit Skript kommen
|
|
Vorschau, Symbolfenster und Palettenfarben dazu. Leer heißt Website-Standard. #}
|
|
{% set btn = row.button|default({}) %}
|
|
<fieldset class="menu-button-fields" data-button-fields>
|
|
<legend>Button</legend>
|
|
<label class="check-label" for="menu-button-{{ n }}"><input id="menu-button-{{ n }}" type="checkbox" data-key="button" name="items[{{ n }}][button]" value="1"{{ row.button ? ' checked' : '' }}> Als Button darstellen</label>
|
|
<input type="hidden" data-key="buttonId" name="items[{{ n }}][buttonId]" value="{{ btn.id|default('') }}">
|
|
<div class="menu-button-options" data-button-options{{ row.button ? '' : ' hidden' }}>
|
|
{% for key, caption in {shape: 'Form', appearance: 'Darstellung', size: 'Größe'} %}
|
|
{% set field = 'button' ~ key|capitalize %}
|
|
<div><label for="menu-{{ field }}-{{ n }}">{{ caption }}</label><select id="menu-{{ field }}-{{ n }}" data-key="{{ field }}" name="items[{{ n }}][{{ field }}]"><option value="">Website-Standard</option>{% for value, name in buttonSchema.buttonOptions[key] %}<option value="{{ value }}"{{ btn[key]|default('') == value ? ' selected' : '' }}>{{ name }}</option>{% endfor %}</select></div>
|
|
{% endfor %}
|
|
{% for field, caption in {buttonFill: 'Fläche', buttonInk: 'Schrift', buttonHoverFill: 'Fläche beim Überfahren', buttonHoverInk: 'Schrift beim Überfahren', buttonIconColor: 'Symbolfarbe'} %}
|
|
{% set stored = {buttonFill: 'fill', buttonInk: 'ink', buttonHoverFill: 'hoverFill', buttonHoverInk: 'hoverInk', buttonIconColor: 'iconColor'}[field] %}
|
|
<div data-color-field><label for="menu-{{ field }}-{{ n }}">{{ caption }}</label><input id="menu-{{ field }}-{{ n }}" data-key="{{ field }}" name="items[{{ n }}][{{ field }}]" value="{{ btn[stored]|default('') }}" placeholder="Website-Standard" pattern="(#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?|@[a-z][a-z0-9-]{0,31})" maxlength="33"></div>
|
|
{% endfor %}
|
|
<div><label for="menu-iconMotion-{{ n }}">Symbol beim Überfahren</label><select id="menu-iconMotion-{{ n }}" data-key="iconMotion" name="items[{{ n }}][iconMotion]">{% for value, name in {none: 'Ruhig', slide: 'Rückt', reveal: 'Erscheint'} %}<option value="{{ value }}"{{ btn.iconMotion|default('none') == value ? ' selected' : '' }}>{{ name }}</option>{% endfor %}</select></div>
|
|
<label class="check-label" for="menu-iconOnly-{{ n }}"><input id="menu-iconOnly-{{ n }}" type="checkbox" data-key="iconOnly" name="items[{{ n }}][iconOnly]" value="1"{{ btn.iconOnly|default(false) ? ' checked' : '' }}> Nur das Symbol zeigen</label>
|
|
<div><label for="menu-buttonMobile-{{ n }}">Auf dem Telefon</label><select id="menu-buttonMobile-{{ n }}" data-key="buttonMobile" name="items[{{ n }}][buttonMobile]">{% for value, name in buttonSchema.mobile %}<option value="{{ value }}"{{ btn.mobile|default('menu') == value ? ' selected' : '' }}>{{ name }}</option>{% endfor %}</select></div>
|
|
</div>
|
|
</fieldset>
|
|
</div>
|
|
</details>
|
|
<div class="menu-entry-actions" role="group" aria-label="{{ row.label }} verschieben oder entfernen">
|
|
<button class="text-button" type="button" data-menu-action="up" aria-label="{{ row.label }} nach oben">↑</button>
|
|
<button class="text-button" type="button" data-menu-action="down" aria-label="{{ row.label }} nach unten">↓</button>
|
|
<button class="text-button" type="button" data-menu-action="indent">Als Unterpunkt</button>
|
|
<button class="text-button" type="button" data-menu-action="outdent">Hauptebene</button>
|
|
<button class="text-button danger-text" type="button" data-menu-action="remove">Entfernen</button>
|
|
</div>
|
|
</li>
|