Translate TYPO3 forms

Translating forms in the TYPO3 Form Framework

Learn how to localize everything related to forms, including some hard to find details.

Introduction

The first time you want to translate a form which was built with EXT:form, you'll probably try to use the usual LLL strings (e.g. 'LLL:EXT:my_ext/Resources/Private/Language/locallang.xlf:localized.label'), which are common in TYPO3. But these cannot be read inside form definitions.

Instead, you'll need to use some special translation keys inside your localization file (locallang.xlf), which are specified by the TYPO3 Form Framework. Only then the translations will be used in your forms.

A translation key is the string which is entered in the id attribute of a <trans-unit> element (inside your localization file).
Example: <trans-unit id="element.email.properties.label">

The official documentation of the Form Framework now contains significant more information on how to provide translations. Still, I wrote this tutorial to complete and supplement some parts of this topic.
I'll explain the basic setup and method first. Then follow the examples for the different properties and components. At last I'll provide a couple of tricks, e.g. how to get a multilingual salutation in the e-mails.

I will keep these examples up-to-date and expand them as needed.

Preparations

To be able to create translations, you'll need one or more .xlf files which you then define in your form configuration. I recommend to prepare a dedicated TYPO3 extension just for your forms, form templates and respective configurations. The basic setup of such an extension is described in the first tutorial of the Form series.

The basic operation of translation files (locallang.xlf) is well explained in this documentation.

EXT:form already provides a language file with several keys. You'll have to load both the original and your own language file(s) inside your YAML file containing the form configuration, below the translationFile setting as an array:

TYPO3:
  CMS:
    Form:
      prototypes:
        standard:
          formElementsDefinition:
            Form:
              renderingOptions:
                translation:
                  translationFile:
                    # Default translation file for the frontend:
                    10: 'EXT:form/Resources/Private/Language/locallang.xlf'
                    # Load your own translation file(s) as desired:
                    20: 'EXT:form_examples/Resources/Private/Language/locallang.xlf'

You must load the default language file from EXT:form, too! By default, the language file is not loaded as part of an array (for compatibility reasons).

Explanation

First of all, an example form definition

Below is the form definition of a simple contact form. As you can see, all elements (renderables) have a certain type (Page, Text, ...) as well as a freely selectable identifier. Other parts like the finishers only have an identifier.

In the following examples we will translate some of these elements by addressing the identifier or type.

renderingOptions:
  submitButtonLabel: Submit
identifier: MyForm
label: 'My form example'
type: Form
prototypeName: standard
finishers:
  -
    options:
      subject: 'Message from your website'
      recipientAddress: your.company@example.com
      recipientName: 'Your Company name'
      senderAddress: '{email}'
      senderName: '{lastname}'
      replyToAddress: ''
      carbonCopyAddress: ''
      blindCarbonCopyAddress: ''
      format: html
      attachUploads: true
    identifier: EmailToReceiver
  -
    options:
      message: 'Thank you for your message! We will get back to you as soon as possible.'
    identifier: Confirmation
renderables:
  -
    renderingOptions:
      previousButtonLabel: 'Previous step'
      nextButtonLabel: 'Neue Seite'
    identifier: page-1
    label: 'Contact Form'
    type: Page
    renderables:
      -
        properties:
          options:
            male: Mr
            female: Ms
          prependOptionLabel: 'Please choose ...'
          fluidAdditionalAttributes:
            required: required
        type: SingleSelect
        identifier: title
        label: Title
        validators:
          -
            identifier: NotEmpty
      -
        defaultValue: ''
        identifier: lastname
        label: 'Last name'
        type: Text
        properties:
          fluidAdditionalAttributes:
            placeholder: 'Your last name'
        validators:
          -
            identifier: NotEmpty
      -
        defaultValue: ''
        identifier: subject
        label: Subject
        type: Text
        properties:
          fluidAdditionalAttributes:
            placeholder: Subject
        validators:
          -
            identifier: NotEmpty
      -
        defaultValue: ''
        identifier: email
        label: Email
        type: Text
        properties:
          fluidAdditionalAttributes:
            placeholder: 'Email address'
        validators:
          -
            identifier: NotEmpty
          -
            identifier: EmailAddress
      -
        defaultValue: ''
        identifier: message
        label: Message
        type: Textarea
        properties:
          fluidAdditionalAttributes:
            placeholder: ''
        validators:
          -
            identifier: NotEmpty
  -
    renderingOptions:
      previousButtonLabel: 'Previous step'
      nextButtonLabel: 'Neue Seite'
    identifier: summarypage
    label: 'Summary page'
    type: SummaryPage

About translation keys and the fallback chain

The following explanation is similar to the official documentation, as this part can hardly be improved:

As I noted at the beginning, the translation keys used inside the translation file follow a well-defined pattern. This is complemented by several possible fallbacks, which allow to overwrite values for individual forms or elements.

This gives you the following translation options:

  1. You can translate a property for both a specific form and form element
  2. You can translate a property for a specific form element inside various forms
  3. You can translate a property for an element type (e.g. all of type SummaryPage) in various forms

These possibilities are achieved by the following keys:

  1. <form-definition-identifier>.element.<element-identifier>.properties.<property-name>
  2. element.<element-identifier>.properties.<property-name>
  3. element.<element-type>.properties.<property-name>

So what exactly are these keys' components? By taking the example form above:

  • <form-definition-identifier>: The unique identifier of the form, here: "MyForm"
  • <element-type>: All text fields are of type "Text", all summary pages are of type "SummaryPage", etc.
  • <element-identifier>: An identifier for every field is set automatically by the form module in the TYPO3 Backend, but can be fully adjusted to your needs. So you could set 'lastname' as the identifier for the relevant field in all your forms, to translate them consistent.
  • <property-name>: The properties of every field, like label or placeholder

The Form Framework searches for translations in the order mentioned above (from specific to general).

It's only if there is no possible value found in every translation file, that the set value inside the form definition is used.

This allows you to set a common value for e.g. all subject field labels and then overwrite this for a single form.

In the following, I will list the different parts which can be translated inside the TYPO3 Form Framework.

Translations

Translating form labels

Let's begin with the usual <label> elements for every created form field. First, I will list all applicable translation keys in the order in which they are looked up. This is followed by an example how it's used inside a localization file.

Translation keys:

  1. <form-identifier>.element.<element-name>.properties.label
  2. element.<element-name>.properties.label
  3. element.<element-type>.properties. properties.label

Example:

<trans-unit id="MyForm.element.subject.properties.label">
    <source>Subject</source>
    <target>Betreff</target>
</trans-unit>
<trans-unit id="element.lastname.properties.label">
    <source>Last name</source>
    <target>Nachname</target>
</trans-unit>

Translating select options (drop-downs), radio buttons and multi checkboxes

You could use select lists, for example, to let the user choose the salutation. They consist of a <select> element and several <option> elements. You can translate every existing option as well as the hint which is shown by default (like 'Please choose ..').

Radio buttons and Multi checkboxes are localized in the same way: your defined choices are available below options.

Translation keys:

  1. <form-identifier>.element.<element-name>.properties.prependOptionLabel
  2. element.<element-name>.properties.prependOptionLabel
  1. <form-identifier>.element.<element-name>.properties.options.<value>
  2. element.<element-name>.properties.options.<value>

Examples:

<trans-unit id="element.title.properties.prependOptionLabel">
    <source>Please choose ...</source>
    <target>Bitte wählen ...</target>
</trans-unit>
<trans-unit id="element.title.properties.options.male">
    <source>Mr</source>
    <target>Herr</target>
</trans-unit>
<trans-unit id="element.title.properties.options.female">
    <source>Ms</source>
    <target>Frau</target>
</trans-unit>

Translating placeholder attributes

Placeholder attributes differ from labels only in the property name at the end.

Translation keys:

  1. <form-identifier>.element.<element-name>.properties.placeholder
  2. element.<element-name>.properties.placeholder
  3. element.<element-type>.properties.placeholder

Examples:

<trans-unit id="element.lastname.properties.placeholder">
    <source>Your last name</source>
    <target>Ihr Nachname</target>
</trans-unit>
<trans-unit id="MyForm.element.message.properties.placeholder">
    <source>How can we be of help?</source>
    <target>Wie können wir Ihnen behilflich sein?</target>
</trans-unit>

Translating form buttons

You have to consider some differences with the buttons. If you use the form-identifier for the submit button, it has to be the second part of the key. This seems to be a bug (as of October 2018).

The previous and next buttons have characteristics as well:

  1. These labels are set separately for normal pages and summary pages.
  2. You cannot use the form-identifier together with the general element-type. It is possible in combination with the element-identifier, though.

For more clarity I separated the universal and the specific translations:

Translation keys (universal):

  • element.Form.renderingOptions.submitButtonLabel
  • element.Page.renderingOptions.previousButtonLabel
  • element.Page.renderingOptions.nextButtonLabel
  • element.SummaryPage.renderingOptions.previousButtonLabel
  • element.SummaryPage.renderingOptions.nextButtonLabel

Examples (universal):

<trans-unit id="element.Form.renderingOptions.submitButtonLabel">
    <source>Send message</source>
    <target>Nachricht senden</target>
</trans-unit>
<trans-unit id="element.Page.renderingOptions.nextButtonLabel">
    <source>continue</source>
    <target>weiter</target>
</trans-unit>
<trans-unit id="element.SummaryPage.renderingOptions.previousButtonLabel">
    <source>back</source>
    <target>zurück</target>
</trans-unit>

Translation keys (specific):

  • element.<form-identifier>.renderingOptions.submitButtonLabel
  • <form-identifier>.element.<element-identifier>.renderingOptions.nextButtonLabel
  • element.<element-identifier>.renderingOptions.nextButtonLabel
  • <form-identifier>.element.<element-identifier>.renderingOptions.previousButtonLabel
  • element.<element-identifier>.renderingOptions.previousButtonLabel

Examples (specific):

<trans-unit id="element.MyForm.renderingOptions.submitButtonLabel">
    <source>Send message!</source>
    <target>Nachricht senden!</target>
</trans-unit>
<trans-unit id="MyForm.element.page-1.renderingOptions.nextButtonLabel">
    <source>load summary</source>
    <target>zur Übersicht</target>
</trans-unit>
<trans-unit id="element.summarypage.renderingOptions.previousButtonLabel">
    <source>update</source>
    <target>verbessern</target>
</trans-unit>

Translating finisher options

Of course finishers have to be translated, too. For instance you can localize the e-mail subject. The confirmation message after submit can be translated as well.

Be careful when using the translation option of finishers. You can set a fixed language with this. In this case, the selected frontend language is not considered. A blank value is sufficient – the default language is used in all e-mails. In doubt, just omit the whole option.

Translation keys:

  • <form-identifier>.finisher.<finisher-identifier>.<option-name>
  • finisher.<finisher-identifier>.<option-name>

Examples:

<trans-unit id=" MyForm.finisher.EmailToSender.subject">
    <source>Your message to ACME Ltd.</source>
    <target>Ihre Nachricht an die ACME GmbH</target>
</trans-unit>
<trans-unit id="finisher.EmailToReceiver.subject">
    <source>Message from website</source>
    <target>Nachricht von Website</target>
</trans-unit>
<trans-unit id="finisher.Confirmation.message">
    <source>Thank you for your message! We will get back to you as soon as possible.</source>
    <target>Vielen Dank für Ihre Nachricht! Wir werden uns schnellstmöglich bei Ihnen melden.</target>
</trans-unit>

Translating content elements in forms

When you load a content element in a form with the corresponding field type, you don't have to translate it or interlink it in a special way in the language file. Just translate the content element the way you always do: translate the page on which the content element is located, then localize the latter. Done!

If the content element still shows only in the original language, please check the following configuration:
config.sys_language_overlay = 1
This setting has to be activated. Read here more on this topic.

Translating static text

Just for the sake of completeness, as the principle should now be clear. The property text is addressed.

Translation keys:

  1. <form-identifier>.element.<element-name>.properties.text
  2. element.<element-name>.properties.text
  3. element.<element-type>.properties. properties.text

Examples:

<trans-unit id="element.my-statictext.properties.text">
    <source>This is some static text.</source>
    <target>Dies ist irgendein statischer Textinhalt.</target>
</trans-unit>
<trans-unit id="MyForm.element.my-statictext.properties.text">
    <source>This is some static text.</source>
    <target>Dies ist irgendein statischer Textinhalt.</target>
</trans-unit>

More hints and tips

Translating texts in e-mails

You can translate most of the finisher parts through the options. In customized e-mail templates you can provide further content. The localization of these texts is performed as usual: use the f:translate viewhelper and point to your language file the with a LLL string. The translation key can be chosen freely.

I described in the second tutorial of this series how to set up customized e-mail templates.

Example:

<f:translate key="LLL:EXT:form_examples/Resources/Private/Language/locallang.xlf:email.yourMessageToUs"/>

Multilingual salutation in e-mails

'Dear Ms Smith' – you can also translate a salutation like this, including the last name. You can use the format specifier%s inside the language file as a placeholder for an argument (= printf function). This argument (the given last name) is passed by the f:translate viewhelper.

In the e-mail template you can use the f:switch viewhelper to choose between the male or female salutation.

locallang.xlf:

<trans-unit id="email.salutation.male">
    <source>Dear Mr %s,</source>
    <target>Sehr geehrter Herr %s,</target>
</trans-unit>
<trans-unit id="email.salutation.female">
    <source>Dear Ms %s,</source>
    <target>Sehr geehrte Frau %s,</target>
</trans-unit>

E-Mail-Template:

<f:switch expression="{form.formState.formValues.title}">
    <f:case value="male">
        <f:translate key="LLL:EXT:form_examples/Resources/Private/Language/locallang.xlf:email.salutation.male"
            arguments="{0: '{form.formState.formValues.lastname}'}"/>
    </f:case>
    <f:case value="female">
        <f:translate key="LLL:EXT:form_examples/Resources/Private/Language/locallang.xlf:email.salutation.female"
            arguments="{0: '{form.formState.formValues.lastname}'}"/>
        </f:case>
    <f:defaultCase>Dear Sir or Madam,</f:defaultCase>
</f:switch>

Remove legends and headings

One last tip: If you don't need the <legend> or <h2> headings in your form, just leave the labels in your form definition blank. In this case, these elements are omitted automatically.

Demo extension!

I have provided a special extension with example forms to download on GitHub. Among other things, it contains various translations just like I described above. It allows you to check out all translation examples (and many more) in a working extension. Just install and start!

EXT:form_examples on GitHub

If you want to create your own forms for a website, I recommend my second form extension as a basis. This contains only the most necessary configurations as a quick start and can be extended as desired.

EXT:form_distribution on GitHub

Back