Dreamdealer
Dreamdealer

Reputation: 188

How to localize Symfony2 forms?

I have a Symfony2 form where a user can enter an address which is now in a fixed format and since we want to offer our software internationally I am looking for the best way to implement this in SF.

Instead of making one generic address type we'd like to localize the forms with one generic format as fallback for unsupported locales.

I want to separate layout and localization (order, grouping, labels, translations, mandatory and optional fields) from the processing (PHP/SF) of the forms and the actual rendering (TWIG).

What I came up was this: create a address form type from a database model which contains all possible fields. Render this form type in twig automatically by calling form_widget(form) or by rendering individual fields when needed. And lastly; define the "layout" of a form in some kind of config format (YML, array, whatever) and extend the default TWIG form rendering to itterate through the form elements defined in said config.

For example the address config for The Netherlands and the US would be:

- NL-nl
  - [firstname, infix, lastname]
  - [street1, number]
  - [postcode, city]
- EN-us
  - [fullname]
  - [street1]
  - [street2]
  - [city, state]
  - [zip]

Later we'll add localized labels, classes, optional and mandatory fields, etc to this configuration.

For now our big question is: where to put this config? Use a simple array in the finishView class? Put the config in a YML file that's parsed by the form types that need a localized form layout?

Any information from people that encountered this problem would be appreciated.

Upvotes: 4

Views: 716

Answers (2)

yceruto
yceruto

Reputation: 9575

Later we'll add localized labels, classes, optional and mandatory fields, etc to this configuration.

The localized labels is automatic process where they can be translated and converted based on the user's locale. The mandatory fields could be resolved using constraints.

where to put this config?

In my opinion this should be made directly into the entity and form type:

You can create an LocaleAddressType and to add these fields depending on current "locale" value, but first, you needs to have an Entity with all possible fields related to "address" approach.

The LocaleAddress entity:

// src/AppBundle/Entity/LocaleAddress.php

use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Table()
 * @ORM\Entity()
 */
class LocaleAddress
{
    /**
     * @var string
     *
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @Assert\NotBlank(groups={"nl-NL", ...})
     * @ORM\Column(type="string", nullable=true)
     */
    private $firstName;

    // ...
}

Includes all distinct fields for all possible locales and settings these with nullable=true. Plus, use the NotBlank constraint and groups option to validate required fields per locale.

The config file

# AppBundle/Resources/config/locale_address_form.yml
address_form:
   nl-NL:
      - [firstname, infix, lastname]
      - [street1, number]
      - [postcode, city]
   en-US:
      - [fullname]
      - [street1]
      - [street2]
      - [city, state]
      - [zip]

The compound LocaleAddressType adds some fields depending on the locale option, by default it is Locale::getDefault()

// src/AppBundle/Form/Type/LocaleAddressType.php

class LocaleAddressType extends AbstractType
{ 
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $config = $this->loadLocaleFormConfig($options['locale']);

        foreach ($config as $fields) {
            foreach (fields as $field) {
                $builder->add($field);
            }
        }
    }

    public function buildView(FormView $view, FormInterface $form, array $options)
    {
        $view->vars['config'] = $this->loadLocaleFormConfig($options['locale']);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'AppBundle\Entity\LocaleAddress',
            'locale', \Locale::getDefault(),
        ]);

        // force that validation groups will equal to configured locale.
        $resolver->setNormalizer('validation_groups', function (Options $options) {
            return [$options['locale']];
        });

        $resolver->setAllowedTypes('locale', ['string']);

        // pending validate the custom locale value.
    }

    private function loadLocaleFormConfig($locale) 
    {
        $config = Yaml::parse(file_get_contents('path/to/locale_address_form.yml'));

        return $config['address_form'][$locale];        
    }
}

Note: On submit that fields are validated in correspondence with the locale value.

Register the form type:

# app/config/services.yml
services:
    app.form.locale_address:
        class: AppBundle\Form\Type\LocaleAddressType
        tags:
            - { name: form.type }

Using the Form

Now, you can use this form type in other forms with localeAddress field associacion.

$build->add('localeAddress', LocaleAddressType::class);

When the locale in your application is changed \Locale::setDefault this will affects the form fields to display.

Creating a Template for the Field

I want to separate layout and localization (order, grouping, labels, translations, mandatory and optional fields) from the processing (PHP/SF) of the forms and the actual rendering (TWIG).

{% block locale_address_widget %}
    {% for fields in config %}
        <div class="row">
        {% set n = 12 // fields|length %}
        {% for field in fields %}
            <div class="col-md-{{ n }}">
                {{ form_row(form[field]) }}
            </div>
        {% endfor %}
        </div>
    {% endfor %}
{% endblock %}

The code is Symfony3-based.

Upvotes: 3

Maciej Mortek
Maciej Mortek

Reputation: 96

There are many options:

  • You can create different form types for each country
  • You can create one form type with all fields and then with form events remove fields which aren't relevated to currently selected country
  • You can create different views per country and one big form type

Upvotes: 0

Related Questions