Skip to content

Symfony UX Translator

Edit this page

EXPERIMENTAL This component is currently experimental and is likely to change, or even change drastically.

Symfony UX Translator is a Symfony bundle providing the same mechanism as Symfony Translator in JavaScript with a TypeScript integration, in Symfony applications. It is part of the Symfony UX initiative.

The ICU Message Format is also supported.

Installation

Note

This package works best with WebpackEncore. To use it with AssetMapper, see be6ffc082da6569f05d6656b208358223b1778ed.

Caution

Before you start, make sure you have StimulusBundle configured in your app.

Install the bundle using Composer and Symfony Flex:

1
$ composer require symfony/ux-translator

After installing the bundle, the following file should be created, thanks to the Symfony Flex recipe:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// assets/translator.js

/*
 * This file is part of the Symfony UX Translator package.
 *
 * If folder "../var/translations" does not exist, or some translations are missing,
 * you must warmup your Symfony cache to refresh JavaScript translations.
 *
 * If you use TypeScript, you can rename this file to "translator.ts" to take advantage of types checking.
 */

import { createTranslator } from '@symfony/ux-translator';
import { messages, localeFallbacks } from '../var/translations/index.js';

const translator = createTranslator({
    messages,
    localeFallbacks,
});

// Allow you to use `import { trans } from './translator';` in your assets
export const { trans } = translator;

Using with WebpackEncore

If you're using WebpackEncore, install your assets and restart Encore (not needed if you're using AssetMapper):

1
2
$ npm install --force
$ npm run watch

Note

For more complex installation scenarios, you can install the JavaScript assets through the @symfony/ux-translator npm package

Using with AssetMapper

Using this library with AssetMapper is possible.

When installing with AssetMapper, Flex will add a new item to your importmap.php file:

1
2
3
'@symfony/ux-translator' => [
    'path' => './vendor/symfony/ux-translator/assets/dist/translator_controller.js',
],

Usage

When warming up the Symfony cache, your translations will be dumped as JavaScript into the var/translations/ directory. For a better developer experience, TypeScript types definitions are also generated aside those JavaScript files.

Then, you will be able to import the trans() function in your assets and use translation keys as simple strings, exactly as you would in your Symfony PHP code.

Configuring the dumped translations

By default, all your translations will be exported. You can restrict the dumped messages by either including or excluding translation domains in your config/packages/ux_translator.yaml file:

1
2
3
4
5
6
7
8
ux_translator:
        domains: ~    # Include all the domains

        domains: foo  # Include only domain 'foo'
        domains: '!foo' # Include all domains, except 'foo'

        domains: [foo, bar]   # Include only domains 'foo' and 'bar'
        domains: ['!foo', '!bar'] # Include all domains, except 'foo' and 'bar'

Disabling TypeScript types dump

By default, TypeScript types definitions are generated alongside the dumped JavaScript translations. This provides autocompletion and type-safety when using the trans() function in your assets.

Even if they are useful when developing, dumping these TypeScript types is useless in production if you use the AssetMapper, because these files will never be used.

You can disable the TypeScript types dump by adding the following configuration:

1
2
3
when@prod:
    ux_translator:
        dump_typescript: false

Configuring the default locale

By default, the default locale is en (English) that you can configure through many ways (in order of priority):

  1. By passing the locale directly to the createTranslator() function
  2. With setLocale('de') or setLocale('de_AT') from your assets/translator.js file
  3. Or with <html data-symfony-ux-translator-locale="{{ app.request.locale }}"> attribute (e.g., de_AT or de using Symfony locale format)
  4. Or with <html lang="{{ app.request.locale|replace({ '_': '-' }) }}"> attribute (e.g., de-AT or de following the W3C specification on language codes)

Detecting missing translations

By default, the translator will return the translation key if the translation is missing.

You can change this behavior by calling setThrowWhenNotFound(true):

1
2
3
4
5
6
7
8
9
10
11
12
13
// assets/translator.js

  import { createTranslator } from '@symfony/ux-translator';
  import { messages, localeFallbacks } from '../var/translations/index.js';

  const translator = createTranslator({
      messages,
      localeFallbacks,
+     throwWhenNotFound: true, // either when creating the translator
  });

+ // Or later in your code
  export const { trans, setThrowWhenNotFound } = translator;

Importing and using translations

If you use the Symfony Flex recipe, you can import the trans() function from the file assets/translator.js.

You can then use translation keys as simple strings, exactly as you would in your Symfony PHP code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// assets/my_file.js

import { trans } from './translator';

// No parameters, uses the default domain ("messages") and the default locale
trans('translation.simple');

// Two parameters "count" and "foo", uses the default domain ("messages") and the default locale
trans('translation.with.parameters', { count: 123, foo: 'bar' });

// No parameters, uses the default domain ("messages") and the default locale
trans('translation.multi.domains');
// Same as above, but uses the "domain2" domain
trans('translation.multi.domains', {}, 'domain2');
// Same as above, but uses the "domain3" domain
trans('translation.multi.domains', {}, 'domain3');

// No parameters, uses the default domain ("messages") and the default locale
trans('translation.multi.locales');
// Same as above, but uses the "fr" locale
trans('translation.multi.locales', {}, 'messages', 'fr');
// Same as above, but uses the "it" locale
trans('translation.multi.locales', {}, 'messages', 'it');

You will get autocompletion and type-safety for translation keys, parameters, domains, and locales.

Q&A

What about bundle size?

All your translations (extracted from the configured domains) are included in the generated var/translations/index.js file, which means they will be included in your final JavaScript bundle).

However, modern build tools, caching strategies, and compression techniques (Brotli, gzip) make this negligible in 2025. Additionally, a future feature will allow filtering dumped translations by pattern for those who need to further reduce bundle size.

Backward Compatibility promise

This bundle aims at following the same Backward Compatibility promise as the Symfony framework: https://symfony.com/doc/current/contributing/code/bc.html

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.
TOC
    Version