14. November 2016

Mehrsprachiges Kontaktformular mit Neos

Ein neues Feature im Flow Form Framework erlaubt es, Formulare via XLIFF-Dateien zu übersetzen. Mit ein paar Anpassungen, lassen sich auch mehrsprachige E-Mail Templates komfortabel nutzen.

Ein eingerichtetes Formular benutzt die Labels, Placeholder usw. so wie sie sind. Um das Formular in der aktuell gewählten Sprache anzeigen zu lassen, muss das Package konfiguriert werden, aus dem die Übersetzungen geladen werden sollen.

Konfiguration

Die einfachste Art das Package für die Translations zu konfigurieren, geht über das verwendete form preset in der Settings.yaml

TYPO3:
  Form:
    presets:
      default:
        formElementTypes:
          'TYPO3.Form:Base':
            renderingOptions:
              translationPackage: 'AcmeCom.SomePackage'

XLIFF Dateien

Die XLIFF-Dateien folgen den üblichen Regeln. Verwendet wird der Main Katalog. Das Form Package beinhaltet bereits den folgenden Katalog (Main.xlf). Um sicher zu gehen, dass die drei erwarteten Labels verfügbar sind, sollte der Katalog kopiert und um die eigenen Labels erweitert werden.

<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file original="" product-name="TYPO3.Form" source-language="en" datatype="plaintext">
        <body>
            <trans-unit id="forms.navigation.previousPage" xml:space="preserve">
                <source>Previous page</source>
            </trans-unit>
            <trans-unit id="forms.navigation.nextPage" xml:space="preserve">
                <source>Next page</source>
            </trans-unit>
            <trans-unit id="forms.navigation.submit" xml:space="preserve">
                <source>Submit</source>
            </trans-unit>
        </body>
    </file>
</xliff>

Für verlässliche Übersetzungen sollten die id-Eigenschaft der Units auf Basis der Form-Konfiguration vergeben werden. Das Schema funktioniert wie folgt:

  • forms.navigation.nextPage / .previousPage In mehrseitigen Formularen wird dies für die Navigation verwendet.
  • forms.navigation.submit Dieser String wird für den Submit-Button verwendet.

Übersetzungen von Labels für Formulare und Sektionen können wie folgt verwendet werden, wobei {identifier} die Kennzeichnung des jeweiligen Formulars bzw. Sektion ist:

forms.pages.{identifier}.label Verwendetes Label für eine Formular-Seite.

forms.sections.{identifier}.label Verwendetes Label für eine Formular-Sektion.

Die IDs der eigentlichen Elemente eines Formulars werden durch Voranstellen von forms.elements.{identifier} wie folgt zusammengebaut, wobei {identifier} die Kennzeichnung des Form-Elements selbst ist.

forms.elements.{identifier}

  • .label Das Label eines Elements.
  • .placeholder Der Platzhalter eines Elements wenn anwendbar.
  • .description Die Beschreibung eines Elements.
  • .text Der Text eines ``StaticText`` Elements.

Die Labels von Radio-Buttons und Optionen von Select-Feldern können nach dem folgenden Schema übersetzt werden, wobei {identifier} die Kennzeichnung des Form-Elements selbst ist und {value} der zugewiesene Wert einer Option.

  • forms.elements.{identifier}.options.{value}

Die ID des Betreff für die Email Finisher wird durch das Voranstellen von `forms.emailFinisher.{identifier}` wie folgt zusammengebaut, wobei `{identifier}` die Position des Finishers im Finisher-Set beschreibt:

forms.emailFinisher.{identifier}

  • .subject

Vollständiges Bespiel

Ein Kontaktformular mit zwei Input-Feldern, einer Selectbox und einem Textfeld sowie zwei Email Finishern.

* Contact Form (Form)
    * Page 01 (Page)
        * Name (Single-line Text)
        * Email (Single-line Text)
        * Contact Reason (Single-select Dropdown)
        * Message (Multi-line Text)

contact.yaml

identifier: 'contact'
label: 'Contact form'
renderables:
  -
    type: 'TYPO3.Form:Page'
    identifier: 'page-one'
    renderables:
      -
        type: 'TYPO3.Form:SingleLineText'
        identifier: name
        label: 'Name'
        validators:
          - identifier: 'TYPO3.Flow:NotEmpty'
        properties:
          placeholder: 'Please enter your full name'
      -
        type: 'TYPO3.Form:SingleLineText'
        identifier: email
        label: 'Email'
        validators:
          - identifier: 'TYPO3.Flow:NotEmpty'
          - identifier: 'TYPO3.Flow:EmailAddress'
        properties:
          placeholder: 'Enter a valid email address'
      -
        type: 'TYPO3.Form:SingleSelectDropdown'
        identifier: 'reason'
        label: 'Reason'
        validators:
          - identifier: 'TYPO3.Flow:NotEmpty'
        properties:
          options:
            '': 'Choose an option ...'
            'general': 'General Question'
            'product': 'Question concerning a product'
      -
        type: 'TYPO3.Form:MultiLineText'
        identifier: message
        label: 'Message'
        validators:
          - identifier: 'TYPO3.Flow:NotEmpty'
        properties:
          placeholder: 'Enter your message here'

finishers:
  -
    identifier: 'TYPO3.Form:Email'
    options:
      templatePathAndFilename: resource://AcmeCom.SomePackage/Private/Templates/Form/Contact.txt
      subject: 'Your Customer Care subject'
      recipientAddress: 'info@acme.com'
      recipientName: 'Acme Customer Care'
      senderAddress: '{email}'
      senderName: '{name}'
      format: plaintext
      translation.enabled: true
      translation.package: 'AcmeCom.SomePackage'
      translation.locale: 'en'
      translation.source: 'Main'
	-
    identifier: 'TYPO3.Form:Email'
    options:
      templatePathAndFilename: resource://AcmeCom.SomePackage/Private/Templates/Form/Contact.txt
      subject: 'Your Administration subject'
      recipientAddress: 'admin@acme.com'
      recipientName: 'Acme Administration'
      senderAddress: '{email}'
      senderName: '{name}'
      format: plaintext
      translation.enabled: true
      translation.package: 'AcmeCom.SomePackage'
      translation.locale: 'en'
      translation.source: 'Main'

Die translation.* Optionen für den Email Finisher sind optional. Die default Werte sind wie folgt:

  • translation.enabled: FALSE
  • translation.package: TYPO3.Form:Base (siehe Setting translationPackage von TYPO3.Form:Base
  • translation.locale: gegenwärtige Locale

Contact.txt

{f:translate(id: "email.contact.text", package: "{translation.package}", source: "{translation.source}", locale: "{translation.locale}")}:

{f:translate(id: "forms.elements.contactReason.label", package: "{translation.package}", source: "{translation.source}", locale: "{translation.locale}")}:       {translatedFormState.formValues.contactReason}
{f:translate(id: "forms.elements.name.label", package: "{translation.package}", source: "{translation.source}", locale: "{translation.locale}")}:    {translatedFormState.formValues.name}
{f:translate(id: "forms.elements.email.label", package: "{translation.package}", source: "{translation.source}", locale: "{translation.locale}")}:             {translatedFormState.formValues.email}
{f:translate(id: "forms.elements.fon.label", package: "{translation.package}", source: "{translation.source}", locale: "{translation.locale}")}:      {translatedFormState.formValues.fon}

{f:translate(id: "forms.elements.message.label", package: "{translation.package}", source: "{translation.source}", locale: "{translation.locale}")}

{translatedFormState.formValues.message}

Main.xlf

<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
	<file original="" product-name="AcmeCom.SomePackage" source-language="en" datatype="plaintext">
		<body>
			<!-- contact reason-->
			
			<trans-unit id="forms.elements.contactReason.label" xml:space="preserve">
				<source>Your Contact Reason</source>
            </trans-unit>
			<trans-unit id="forms.elements.contactReason.options." xml:space="preserve">
				<source>Please choose a contact reason</source>
 			</trans-unit>
			<trans-unit id="forms.elements.contactReason.options.general" xml:space="preserve">
				<source>Common question</source>
 			</trans-unit>
			<trans-unit id="forms.elements.contactReason.options.product" xml:space="preserve">
				<source>Product request</source>
 			</trans-unit>
 			
 			<!-- name -->
	
			<trans-unit id="forms.elements.name.label">
				<source>Your name</source>
			</trans-unit>
			<trans-unit id="forms.elements.name.placeholder">
				<source>Your name</source>
			</trans-unit>

			<!-- email -->

			<trans-unit id="forms.elements.email.label">
				<source>Your name</source>
			</trans-unit>
			<trans-unit id="forms.elements.email.placeholder">
				<source>Your name</source>
			</trans-unit>
			
			<!-- message -->
			
			<trans-unit id="forms.elements.message.label">
				<source>Your message</source>
			</trans-unit>
			
			<!-- subject -->
	
			<trans-unit id="forms.emailFinisher.0.subject">
				<source>Your request to AcmeCom</source>
			</trans-unit>
			<trans-unit id="forms.emailFinisher.1.subject">
				<source>Request by contact form</source>
			</trans-unit>
			
			<!-- email text -->
			
			<trans-unit id="email.contact.text">
				<source>The contact form has been sent with the following values:</source>
			</trans-unit>
		</body>
	</file>
</xliff>

Links

14. November 2016
Kai Möller

Kai Möller