Translations ¶
The term "internationalization" (often abbreviated i18n) refers to the process of abstracting strings and other locale-specific pieces out of your application into a layer where they can be translated and converted based on the user's locale (i.e. language and country). For text, this means wrapping each with a function capable of translating the text (or "message") into the language of the user:
1 2 3 4 5 6 |
// text will *always* print out in English
echo 'Hello World';
// text can be translated into the end-user's language or
// default to English
echo $translator->trans('Hello World');
|
Note
The term locale refers roughly to the user's language and country. It
can be any string that your application uses to manage translations and
other format differences (e.g. currency format). The ISO 639-1
language code, an underscore (_
), then the ISO 3166-1 alpha-2
country code (e.g. fr_FR
for French/France) is recommended.
The translation process has several steps:
- Enable and configure Symfony's
translation service;Symfony の翻訳サービスを有効にして構成します。
- Abstract strings (i.e. "messages") by wrapping them in calls to the
Translator
("Translations");theTranslator ("Translations") への呼び出しでそれらをラップすることにより、文字列 (つまり、"メッセージ") を抽象化します。 - Create translation resources/files
for each supported locale that translate each message in the application;アプリケーション内の各メッセージを翻訳する、サポートされているロケールごとに翻訳リソース/ファイルを作成します。
- Determine, set and manage the user's locale
for the request and optionally
on the user's entire session.リクエスト、およびオプションでユーザーのセッション全体のユーザーのロケールを決定、設定、および管理します。
Installation ¶
First, run this command to install the translator before using it:
1 |
$ composer require symfony/translation
|
Configuration ¶
The previous command creates an initial config file where you can define the default locale of the application and the directory where the translation files are located:
-
YAML
YAML
-
XML
XML
-
PHP
PHP
1 2 3 4 5 |
# config/packages/translation.yaml
framework:
default_locale: 'en'
translator:
default_path: '%kernel.project_dir%/translations'
|
The locale used in translations is the one stored on the request. This is
typically set via a _locale
attribute on your routes (see How to Work with the User's Locale).
Basic Translation ¶
Translation of text is done through the translator
service
(Translator). To translate a block
of text (called a message), use the
trans() method. Suppose,
for example, that you're translating a static message from inside a controller:
1 2 3 4 5 6 7 8 9 |
// ...
use Symfony\Contracts\Translation\TranslatorInterface;
public function index(TranslatorInterface $translator)
{
$translated = $translator->trans('Symfony is great');
// ...
}
|
When this code is run, Symfony will attempt to translate the message
"Symfony is great" based on the locale
of the user. For this to work,
you need to tell Symfony how to translate the message via a "translation
resource", which is usually a file that contains a collection of translations
for a given locale. This "dictionary" of translations can be created in several
different formats:
-
YAML
YAML
-
XML
XML
-
PHP
PHP
1 2 |
# translations/messages.fr.yaml
Symfony is great: J'aime Symfony
|
For information on where these files should be located, see Translations.
Now, if the language of the user's locale is French (e.g. fr_FR
or fr_BE
),
the message will be translated into J'aime Symfony
. You can also translate
the message inside your templates.
Using Real or Keyword Messages ¶
This example illustrates the two different philosophies when creating messages to be translated:
1 2 3 |
$translator->trans('Symfony is great');
$translator->trans('symfony.great');
|
In the first method, messages are written in the language of the default locale (English in this case). That message is then used as the "id" when creating translations.
In the second method, messages are actually "keywords" that convey the
idea of the message. The keyword message is then used as the "id" for
any translations. In this case, translations must be made for the default
locale (i.e. to translate symfony.great
to Symfony is great
).
The second method is handy because the message key won't need to be changed in every translation file if you decide that the message should actually read "Symfony is really great" in the default locale.
The choice of which method to use is entirely up to you, but the "keyword" format is often recommended for multi-language applications, whereas for shared bundles that contain translation resources we recommend the real message, so your application can choose to disable the translator layer and you will see a readable message.
Additionally, the php
and yaml
file formats support nested ids to
avoid repeating yourself if you use keywords instead of real text for your
ids:
-
YAML
YAML
-
PHP
PHP
1 2 3 4 5 6 7 8 9 10 11 12 |
symfony:
is:
# id is symfony.is.great
great: Symfony is great
# id is symfony.is.amazing
amazing: Symfony is amazing
has:
# id is symfony.has.bundles
bundles: Symfony has bundles
user:
# id is user.login
login: Login
|
The Translation Process ¶
To actually translate the message, Symfony uses the following process when
using the trans()
method:
- The
locale
of the current user, which is stored on the request is determined;要求に格納されている現在のユーザーのロケールが決定されます。 - A catalog (e.g. big collection) of translated messages is loaded from translation
resources defined for the
locale
(e.g.fr_FR
). Messages from the fallback locale are also loaded and added to the catalog if they don't already exist. The end result is a large "dictionary" of translations. This catalog is cached in production to minimize performance impact.翻訳されたメッセージのカタログ (大きなコレクションなど) は、ロケール用に定義された翻訳リソース (fr_FR など) からロードされます。フォールバック ロケールからのメッセージも読み込まれ、カタログにまだ存在しない場合は追加されます。最終結果は、翻訳の大きな「辞書」です。このカタログは、パフォーマンスへの影響を最小限に抑えるために本番環境でキャッシュされます。 - If the message is located in the catalog, the translation is returned. If
not, the translator returns the original message.メッセージがカタログにある場合は、翻訳が返されます。そうでない場合、翻訳者は元のメッセージを返します。
Tip
When translating strings that are not in the default domain (messages
),
you must specify the domain as the third argument of trans()
:
1 |
$translator->trans('Symfony is great', [], 'admin');
|
Message Format ¶
Sometimes, a message containing a variable needs to be translated:
1 2 |
// ...
$translated = $translator->trans('Hello '.$name);
|
However, creating a translation for this string is impossible since the translator will try to look up the message including the variable portions (e.g. "Hello Ryan" or "Hello Fabien").
Another complication is when you have translations that may or may not be plural, based on some variable:
1 2 |
There is one apple.
There are 5 apples.
|
To manage these situations, Symfony follows the ICU MessageFormat syntax by using PHP's MessageFormatter class. Read more about this in How to Translate Messages using the ICU MessageFormat.
Tip
If you don't use the ICU MessageFormat syntax in your translation files, pass a parameter named "%count%" to select the best plural form of the message:
1 |
{{ message|trans({'%name%': '...', '%count%': 1}, 'app') }}
|
The message
variable must include all the different versions of this
message based on the value of the count
parameter. For example:
1 |
{0}%name% has no apples|{1}%name% has one apple|]1,Inf[ %name% has %count% apples
|
Translatable Objects ¶
Sometimes translating contents in templates is cumbersome because you need the original message, the translation parameters and the translation domain for each content. Making the translation in the controller or services simplifies your templates, but requires injecting the translator service in different parts of your application and mocking it in your tests.
Instead of translating a string at the time of creation, you can use a "translatable object", which is an instance of the TranslatableMessage class. This object stores all the information needed to fully translate its contents when needed:
1 2 3 4 5 6 7 |
use Symfony\Component\Translation\TranslatableMessage;
// the first argument is required and it's the original message
$message = new TranslatableMessage('Symfony is great!');
// the optional second argument defines the translation parameters and
// the optional third argument is the translation domain
$status = new TranslatableMessage('order.status', ['%status%' => $order->getStatus()], 'store');
|
Templates are now much simpler because you can pass translatable objects to the
trans
filter:
1 2 |
<h1>{{ message|trans }}</h1>
<p>{{ status|trans }}</p>
|
Tip
The translation parameters can also be a TranslatableMessage.
Tip
There's also a function called t(), available both in Twig and PHP, as a shortcut to create translatable objects.
Translations in Templates ¶
Most of the time, translation occurs in templates. Symfony provides native support for both Twig and PHP templates.
Using Twig Tags ¶
Symfony provides a specialized Twig tag trans
to help with message
translation of static blocks of text:
1 |
{% trans %}Hello %name%{% endtrans %}
|
Caution
The %var%
notation of placeholders is required when translating in
Twig templates using the tag.
Tip
If you need to use the percent character (%
) in a string, escape it by
doubling it: {% trans %}Percent: %percent%%%{% endtrans %}
You can also specify the message domain and pass some additional variables:
1 2 3 |
{% trans with {'%name%': 'Fabien'} from 'app' %}Hello %name%{% endtrans %}
{% trans with {'%name%': 'Fabien'} from 'app' into 'fr' %}Hello %name%{% endtrans %}
|
Using Twig Filters ¶
The trans
filter can be used to translate variable texts and complex expressions:
1 2 3 |
{{ message|trans }}
{{ message|trans({'%name%': 'Fabien'}, 'app') }}
|
Tip
Using the translation tags or filters have the same effect, but with
one subtle difference: automatic output escaping is only applied to
translations using a filter. In other words, if you need to be sure
that your translated message is not output escaped, you must apply
the raw
filter after the translation filter:
1 2 3 4 5 6 7 8 9 10 |
{# text translated between tags is never escaped #}
{% trans %}
<h3>foo</h3>
{% endtrans %}
{% set message = '<h3>foo</h3>' %}
{# strings and variables translated via a filter are escaped by default #}
{{ message|trans|raw }}
{{ '<h3>bar</h3>'|trans|raw }}
|
Tip
You can set the translation domain for an entire Twig template with a single tag:
1 |
{% trans_default_domain 'app' %}
|
Note that this only influences the current template, not any "included" template (in order to avoid side effects).
Forcing the Translator Locale ¶
When translating a message, the translator uses the specified locale or the
fallback
locale if necessary. You can also manually specify the locale to
use for translation:
1 2 3 4 5 6 |
$translator->trans(
'Symfony is great',
[],
'messages',
'fr_FR'
);
|
Extracting Translation Contents and Updating Catalogs Automatically ¶
The most time-consuming tasks when translating an application is to extract all
the template contents to be translated and to keep all the translation files in
sync. Symfony includes a command called translation:extract
that helps you
with these tasks:
1 2 3 4 5 6 7 8 |
# shows all the messages that should be translated for the French language
$ php bin/console translation:extract --dump-messages fr
# updates the French translation files with the missing strings for that locale
$ php bin/console translation:extract --force fr
# check out the command help to see its options (prefix, output format, domain, sorting, etc.)
$ php bin/console translation:extract --help
|
The translation:extract
command looks for missing translations in:
- Templates stored in the
templates/
directory (or any other directory defined in the twig.default_path and twig.paths config options);templates/ ディレクトリ (または twig.default_path および twig.paths 設定オプションで定義された他のディレクトリ) に格納されたテンプレート; - Any PHP file/class that injects or autowires
the
translator
service and makes calls to thetrans()
method.トランスレータ サービスを挿入または自動配線し、trans() メソッドを呼び出す任意の PHP ファイル/クラス。 - Any PHP file/class stored in the
src/
directory that creates Translations using the constructor or thet()
method or calls thetrans()
method.コンストラクターまたは t() メソッドを使用して変換を作成するか、trans() メソッドを呼び出す、src/ ディレクトリに格納された任意の PHP ファイル/クラス。 - Any PHP file/class stored in the
src/
directory that uses Constraints Attributes with*message
named argument(s).*message という名前の引数を持つ Constraints Attributes を使用する src/ ディレクトリに格納された PHP ファイル/クラス。
6.2
The support of PHP files/classes that use constraint attributes was introduced in Symfony 6.2.
Translation Resource/File Names and Locations ¶
Symfony looks for message files (i.e. translations) in the following default locations:
- the
translations/
directory (at the root of the project);translations/ ディレクトリ (プロジェクトのルート); - the
translations/
directory inside of any bundle (and also theirResources/translations/
directory, which is no longer recommended for bundles).バンドル内の translations/ ディレクトリ (およびバンドルには推奨されなくなった theirResources/translations/ ディレクトリ)。
The locations are listed here with the highest priority first. That is, you can override the translation messages of a bundle in the first directory.
The override mechanism works at a key level: only the overridden keys need to be listed in a higher priority message file. When a key is not found in a message file, the translator will automatically fall back to the lower priority message files.
The filename of the translation files is also important: each message file
must be named according to the following path: domain.locale.loader
:
- domain: Domains are a way to organize messages into groups. Unless
parts of the application are explicitly separated from each other, it is
recommended to only use the default
messages
domain (e.g.messages.en.yaml
).domain: ドメインは、メッセージをグループに編成する方法です。アプリケーションの一部が互いに明示的に分離されていない限り、デフォルトのメッセージ ドメイン (例:messages.en.yaml) のみを使用することをお勧めします。 - locale: The locale that the translations are for (e.g.
en_GB
,en
, etc);locale: 翻訳対象のロケール (en_GB、en など)。 - loader: How Symfony should load and parse the file (e.g.
xlf
,php
,yaml
, etc).loader: Symfony がファイルをロードして解析する方法 (例: xlf、php、yaml など)。
The loader can be the name of any registered loader. By default, Symfony provides many loaders which are selected based on the following file extensions:
.yaml
: YAML file (you can also use the.yml
file extension);.yaml: YAML ファイル (.yml ファイル拡張子も使用できます);.xlf
: XLIFF file (you can also use the.xliff
file extension);.xlf: XLIFF ファイル (.xliff ファイル拡張子も使用できます);.php
: a PHP file that returns an array with the translations;.php: 翻訳を含む配列を返す PHP ファイル。.csv
: CSV file;.csv: CSV ファイル。.json
: JSON file;.json: JSON ファイル。.ini
: INI file;.ini: INI ファイル。.dat
,.res
: ICU resource bundle;.dat、.res: ICU リソース バンドル。.mo
: Machine object format;.mo: マシン オブジェクト形式。.po
: Portable object format;.po: ポータブル オブジェクト形式。.qt
: QT Translations TS XML file;.qt: QT Translations TS XML ファイル。
6.1
The .xliff
file extension support was introduced in Symfony 6.1.
The choice of which loader to use is entirely up to you and is a matter of taste. The recommended option is to use YAML for simple projects and use XLIFF if you're generating translations with specialized programs or teams.
Caution
Each time you create a new message catalog (or install a bundle that includes a translation catalog), be sure to clear your cache so that Symfony can discover the new translation resources:
1 |
$ php bin/console cache:clear
|
Note
You can add other directories with the paths option in the configuration:
-
YAML
YAML
-
XML
XML
-
PHP
PHP
1 2 3 4 5 |
# config/packages/translation.yaml
framework:
translator:
paths:
- '%kernel.project_dir%/custom/path/to/translations'
|
Note
You can also store translations in a database, or any other storage by providing a custom class implementing the LoaderInterface interface. See the Built-in Symfony Service Tags tag for more information.
Translation Providers ¶
When using external translators to translate your application, you must send them the new contents to translate frequently and merge the results back in the application.
Instead of doing this manually, Symfony provides integration with several third-party translation services (e.g. Crowdin or Lokalise). You can upload and download (called "push" and "pull") translations to/from these services and merge the results automatically in the application.
Installing and Configuring a Third Party Provider ¶
Before pushing/pulling translations to a third-party provider, you must install the package that provides integration with that provider:
Provider | Install with |
---|---|
Crowdin | composer require symfony/crowdin-translation-provider |
Loco (localise.biz) | composer require symfony/loco-translation-provider |
Lokalise | composer require symfony/lokalise-translation-provider |
Each library includes a Symfony Flex recipe that will add
a configuration example to your .env
file. For example, suppose you want to
use Loco. First, install it:
1 |
$ composer require symfony/loco-translation-provider
|
You'll now have a new line in your .env
file that you can uncomment:
1 2 |
# .env
LOCO_DSN=loco://API_KEY@default
|
The LOCO_DSN
isn't a real address: it's a convenient format that offloads
most of the configuration work to Symfony. The loco
scheme activates the
Loco provider that you just installed, which knows all about how to push and
pull translations via Loco. The only part you need to change is the
API_KEY
placeholder.
This table shows the full list of available DSN formats for each provider:
Provider | DSN |
---|---|
Crowdin | crowdin://PROJECT_ID:API_TOKEN@ORGANIZATION_DOMAIN.default |
Loco (localise.biz) | loco://API_KEY@default |
Lokalise | lokalise://PROJECT_ID:API_KEY@default |
To enable a translation provider, add the correct DSN in your .env
file and
configure the providers
option:
-
YAML
YAML
-
XML
XML
-
PHP
PHP
1 2 3 4 5 6 7 8 |
# config/packages/translation.yaml
framework:
translator:
providers:
loco:
dsn: '%env(LOCO_DSN)%'
domains: ['messages']
locales: ['en', 'fr']
|
Tip
If you use Lokalise as provider and a locale format following the ISO 639-1 (e.g. "en" or "fr"), you have to set the Custom Language Name setting in Lokalise for each of your locales, in order to override the default value (which follow the ISO 639-1 succeeded by a sub-code in capital letters that specifies the national variety (e.g. "GB" or "US" according to ISO 3166-1 alpha-2)).
Pushing and Pulling Translations ¶
After configuring the credentials to access the translation provider, you can now use the following commands to push (upload) and pull (download) translations:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# push all local translations to the Loco provider for the locales and domains
# configured in config/packages/translation.yaml file.
# it will update existing translations already on the provider.
$ php bin/console translation:push loco --force
# push new local translations to the Loco provider for the French locale
# and the validators domain.
# it will **not** update existing translations already on the provider.
$ php bin/console translation:push loco --locales fr --domains validators
# push new local translations and delete provider's translations that not
# exists anymore in local files for the French locale and the validators domain.
# it will **not** update existing translations already on the provider.
$ php bin/console translation:push loco --delete-missing --locales fr --domains validators
# check out the command help to see its options (format, domains, locales, etc.)
$ php bin/console translation:push --help
|
1 2 3 4 5 6 7 8 9 10 11 12 |
# pull all provider's translations to local files for the locales and domains
# configured in config/packages/translation.yaml file.
# it will overwrite completely your local files.
$ php bin/console translation:pull loco --force
# pull new translations from the Loco provider to local files for the French
# locale and the validators domain.
# it will **not** overwrite your local files, only add new translations.
$ php bin/console translation:pull loco --locales fr --domains validators
# check out the command help to see its options (format, domains, locales, intl-icu, etc.)
$ php bin/console translation:pull --help
|
Handling the User's Locale ¶
Translating happens based on the user's locale. Read How to Work with the User's Locale to learn more about how to handle it.
Fallback Translation Locales ¶
Imagine that the user's locale is es_AR
and that you're translating the
key Symfony is great
. To find the Spanish translation, Symfony actually
checks translation resources for several locales:
- First, Symfony looks for the translation in a
es_AR
(Argentinean Spanish) translation resource (e.g.messages.es_AR.yaml
);まず、Symfony は es_AR (アルゼンチンスペイン語) 翻訳リソース (messages.es_AR.yaml など) で翻訳を探します。 - If it wasn't found, Symfony looks for the translation in the
parent locale, which is automatically defined only for some locales. In
this example, the parent locale is
es_419
(Latin American Spanish);見つからない場合、Symfony は一部のロケールに対してのみ自動的に定義される親ロケールで翻訳を探します。この例では、親ロケールは es_419 (ラテン アメリカ スペイン語) です。 - If it wasn't found, Symfony looks for the translation in a
es
(Spanish) translation resource (e.g.messages.es.yaml
);見つからなかった場合、Symfony は es(スペイン語) 翻訳リソース (messages.es.yaml など) で翻訳を探します。 If the translation still isn't found, Symfony uses the
fallbacks
option, which can be configured as follows:それでも翻訳が見つからない場合、Symfony は次のように設定できるフォールバック オプションを使用します。-
YAML
YAML
-
XML
XML
-
PHP
PHP
1 2 3 4 5
# config/packages/translation.yaml framework: translator: fallbacks: ['en'] # ...
-
YAML
Note
When Symfony can't find a translation in the given locale, it will add the missing translation to the log file. For details, see Framework Configuration Reference (FrameworkBundle).
Switch Locale Programmatically ¶
6.1
The LocaleSwitcher
was introduced in Symfony 6.1.
Sometimes you need to change the locale of the application dynamically just to run some code. Imagine a console command that renders Twig templates of emails in different languages. You need to change the locale only to render those templates.
The LocaleSwitcher
class allows you to change at once the locale
of:
- All the services that are tagged with
kernel.locale_aware
;kernel.locale_aware でタグ付けされたすべてのサービス。 \Locale::setDefault()
;\Locale::setDefault();- If a request is available, the
_locale
request attribute.リクエストが利用可能な場合、_locale リクエスト属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
use Symfony\Component\Translation\LocaleSwitcher;
class SomeService
{
private LocaleSwitcher $localeSwitcher;
public function __construct(LocaleSwitcher $localeSwitcher)
{
$this->localeSwitcher = $localeSwitcher;
}
public function someMethod()
{
// you can get the current application locale like this:
$currentLocale = $this->localeSwitcher->getLocale();
// you can set the locale for the entire application like this:
// (from now on, the application will use 'fr' (French) as the
// locale; including the default locale used to translate Twig templates)
$this->localeSwitcher->setLocale('fr');
// reset the current locale of your application to the configured default locale
// in config/packages/translation.yaml, by option 'default_locale'
$this->localeSwitcher->reset();
// you can also run some code with a certain locale, without
// changing the locale for the rest of the application
$this->localeSwitcher->runWithLocale('es', function() {
// e.g. render here some Twig templates using 'es' (Spanish) locale
});
// ...
}
}
|
When using autowiring, type-hint any controller or
service argument with the LocaleSwitcher
class to inject the locale switcher service. Otherwise, configure your services
manually and inject the translation.locale_switcher
service.
Translating Database Content ¶
The translation of database content should be handled by Doctrine through the Translatable Extension or the Translatable Behavior (PHP 5.4+). For more information, see the documentation for these libraries.
Debugging Translations ¶
When you work with many translation messages in different languages, it can be hard to keep track which translations are missing and which are not used anymore. Read How to Find Missing or Unused Translation Messages to find out how to identify these messages.
Summary ¶
With the Symfony Translation component, creating an internationalized application no longer needs to be a painful process and boils down to these steps:
- Abstract messages in your application by wrapping each in the
trans() method;trans() メソッドでそれぞれをラップして、アプリケーション内のメッセージを抽象化します。
- Translate each message into multiple locales by creating translation message
files. Symfony discovers and processes each file because its name follows
a specific convention;翻訳メッセージ ファイルを作成して、各メッセージを複数のロケールに翻訳します。 symfony は、その名前が特定の規則に従っているため、各ファイルを検出して処理します。
- Manage the user's locale, which is stored on the request, but can also
be set on the user's session.ユーザーのロケールを管理します。これはリクエストに保存されますが、ユーザーのセッションでも設定できます。
Learn more ¶
- How to Translate Messages using the ICU MessageFormatICU MessageFormat を使用してメッセージを翻訳する方法
- How to Work with the User's Localeユーザーのロケールを操作する方法
- How to Find Missing or Unused Translation Messages欠落または未使用の翻訳メッセージを見つける方法
- How to Find Errors in Translation Files翻訳ファイルのエラーを見つける方法
- The XLIFF formatXLIFF形式