symfony-docs
symfony-docs copied to clipboard
Documentation is confusing about use of {placeholders} in translations
I was looking at the documentation about translations here: https://symfony.com/doc/5.x/translation.html#basic-translation
and I was looking for the part about parameters, or should I say placeholders. What I mean with parameters/placeholders is when you have a phrase somephrase
whose translation is "Hello {username}
and you call:
$translator->trans('somephrase', ['username' => 'John']) ;
ane the result is "Hello John"
.
I think those are called Placeholders.
So, the only place where the page above says something about that is here: https://symfony.com/doc/5.x/translation.html#message-format
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.
So, I follow that link and it says:
In order to use the ICU Message Format, the message domain has to be suffixed with +intl-icu:
and then it explains, among other things, how placeholders work, besides pluralization and other stuff.
This seems to be very clear: in order to use placeholders, as well as pluralization and other features, the message domains has to be suffixed with +intl-icu
However, I have tried using basic translations with placeholders with the default domain messages
without any suffix, and placeholders work. I don't know about pluralization and other stuff.
So, the documentation wrongly suggests that, in order to be able to use placeholders, you need to suffix the domain names with that string, but that's not true.
I don't know if that's true for other features such as pluralization; if that's the case, then it's not clear.
It seems to be even worse than that.
Apparently, this:
$translator->trans('somephrase', ['username' => 'John']) ;
will not replace occurrences of the string "{username}"
but rather of the string "username"
.
That is, the second argument passed to Translator::trans()
is just an array of arbitrary replacements where the keys can be any strings, not necessarily wrapped in {}
.
This isn't mentioned in the documentation, nor does it explain how this interferes with messages that do use the ICU format.
I think there is/may be typo indeed here https://symfony.com/doc/current/reference/formats/message_format.html#message-placeholders Should be {name} as key parameter to match translation message containing placeholder At least that how i use it on my projects Perhaps the translator handle under the hood the addition of { } if not provided but can be misleading if so
I think there is/may be typo indeed here
I'm not sure. Could it be that:
- when yo do use the ICU format (i.e. a domain name suffixed with
+intl-icu
), placeholders have to be wrapped in{}
in the translations, and the bare names without{}
should be used as keys in the parameters, - when you do not use the ICU format (i.e. a domain without the suffix), all the translator does is blindly replace the parameter keys with the parameter values, whatever the keys are, and the
{}
have no special meaning (so if you want{name}
to be replaced you pass'['{name}' => 'john'']
as parameters).
In fact I'm pretty sure the second half is true (the source code seems to be using strstr
, and I tested it) but I haven't checked the first half.
At least that how i use it on my projects
Do you use the ICU format?
I use icu in some of them, mostly with twig; after i upgraded from older logic I will double check tomorrow (on computer) on how my file are named and how i use trans methods Perhaps i myself am confused with this prefix and how i call in the code
I've checked and I use things like the doc
# emails+intl-icu.fr.yaml
proposition_accepted_user.subject: "{company_name} - foo bar n°{number}"
{{ 'proposition_accepted_user.subject'|trans({
'company_name': 'from.name'|trans({}, locale = locale),
'number': number
}, locale = locale) }}
so to me I only use the "full" ICU logic now, I was confusing probably with old maner
Yes, the built-in non-standard Symfony way for translations is not documented.
For the not ICU MessageFormat filenames, you can use Twig like placeholders:
# translations/messages.en.yaml
say_hello: Hello %name%. Symfony is %what%!
// prints "Hello Fabien. Symfony is awesome!"
echo $translator->trans('say_hello', ['%name%' => 'Fabien', '%what%' => 'awesome'])
The Translator engine will replace everything so this would work also:
# translations/messages.en.yaml
say_hello: Hello name. Symfony is {what}!
// prints "Hello Fabien. Symfony is awesome!"
echo $translator->trans('say_hello', ['name' => 'Fabien', '{what}' => 'awesome'])
The same about pluralization which was documented in the Symfony 1. Currently, it's still in the code and tests, but not documented or deprecated.
Turns a string with “|
” separators into an array and selects the appropriate translation based on the location and %count%
value. The selection is based on the position (0-5) in the table or the value in {}
.
The |whatever:
fragment is ignored and is descriptive.
# translations/messages.en.yaml
num_of_apples: 'There is one apple|There are %count% apples'
# the same as the above
# num_of_apples: 'one: There is one apple|more: There are %count% apples'
# The condition with 0
# num_of_apples: '{0} There are no apples|one: There is one apple|other: There are %count% apples'
// prints "There is one apple"
echo $translator->trans('num_of_apples', ['%count%' => 1])
The var must be named %count%
.
There is only a note about ranges, but they still work.
ICU MessageFormat is standardized and covers more cases like genders. Only ranges are missing.
@javiereguiluz The Translator documentation is based on normal file name examples, so why this is not documented?