Skip to main content
Oro products support a multitude of localizations and regional customizations depending on the language and region of the audience. For example, management can interact with the backend in one language while website visitors see content customized for their particular language and currency.

Translation approaches

There are three strategies for translating content in Oro applications. Choose based on whether the content is static, dynamic dictionary data, or localization-specific content:

Symfony Translator

Best for static UI text — form labels, flash messages, and interface strings. Editable via the Oro translations UI.

Gedmo Translatable

Best for dynamic dictionary data (e.g., Country, Region names) that must be filterable and sortable in data grids.

LocalizedFallbackValue

Best for content that needs separate values per Localization, with a fallback chain through parent localizations.

Approach comparison

Symfony TranslatorGedmo TranslatableLocalizedFallbackValue
Static contentYesNoNo
Dynamic contentNoYesYes
Grid filtering/sortingN/AYesNo (requires extra work)
Per-localization valuesNoNoYes
Editable in UI without language switchYesNoYes

Symfony Translator (static content)

Add translations to Resources/translations/messages.en.yml:
oro:
   translation:
       some_field:
           label: Localized value
Use in a Twig template:
{{ 'oro.translation.some_field.label'|trans }}
Use in PHP code:
return [
    'label' => $this->translator->trans('oro.translation.some_field.label')
];

Gedmo Translatable (dynamic dictionary data)

Mark entity fields with #[Gedmo\Translatable] and implement a translation entity:
#[ORM\Entity]
#[ORM\Table('acme_demo_country')]
#[Gedmo\TranslationEntity(class: 'Acme\Bundle\DemoBundle\Entity\CountryTranslation')]
class Country implements Translatable
{
    #[ORM\Column(name: 'name', type: 'string', length: 255)]
    #[Gedmo\Translatable]
    protected string $name;

    #[Gedmo\Locale]
    protected string $locale;
    // ...
}
To enable translation in a data grid, add the HINT_TRANSLATABLE query hint:
datagrids:
   acme-country-grid:
       source:
           type: orm
           query:
               select:
                   - country.id
                   - country.name
               from:
                   - { table: Acme\Bundle\DemoBundle\Entity\Country, alias: country }
           hints:
               - HINT_TRANSLATABLE
       columns:
           name:
               label: Country Name
       sorters:
           columns:
               name:
                   data_name: country.name
       filters:
           columns:
               name:
                   type: string
                   data_name: country.name

LocalizedFallbackValue (per-localization content)

Use LocalizedFallbackValue for content that needs separate values per Localization:
use Oro\Bundle\LocaleBundle\Entity\LocalizedFallbackValue;

#[ORM\Entity]
#[ORM\Table(name: 'acme_demo_some')]
class Some implements ExtendEntityInterface
{
    use ExtendEntityTrait;

    #[ORM\ManyToMany(
        targetEntity: 'Oro\Bundle\LocaleBundle\Entity\LocalizedFallbackValue',
        cascade: ['ALL'],
        orphanRemoval: true
    )]
    #[ORM\JoinTable(name: 'acme_demo_some_name')]
    protected $names;
}
Register the fallback field in the bundle class:
use Oro\Bundle\LocaleBundle\DependencyInjection\Compiler\EntityFallbackFieldsStoragePass;

class AcmeDemoBundle extends Bundle
{
    public function build(ContainerBuilder $container): void
    {
        parent::build($container);
        $container->addCompilerPass(new EntityFallbackFieldsStoragePass([
            'Acme\Bundle\DemoBundle\Entity\Some' => [
                'name' => 'names'
            ]
        ]));
    }
}
Use LocalizedFallbackValueCollectionType in the form:
use Oro\Bundle\LocaleBundle\Form\Type\LocalizedFallbackValueCollectionType;

class SomeType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('names', LocalizedFallbackValueCollectionType::class, [
            'label' => 'acme_demo.some.names.label'
        ]);
    }
}

Additional topics

Translation Configuration

Configure debug translator, debug JS translations, and translation cache settings.

Data Fixtures

Load translatable entity data using AbstractTranslatableEntityFixture.

Schema Migrations

Add translation-related columns and tables in versioned migration scripts.

Localization

Configure locale data, name formatting, address formatting, and date/numeric formatting.

Add Translations to Source Code

Best practices for adding translation keys directly in PHP, Twig, and JS files.

Build docs developers (and LLMs) love