I need to translate variable-length sentences in Symfony 3.4.
Sample sentence: "Included: 4 breakfasts, 1 brunch and 3 dinners."
For the exemple let's say the following constraints:
The keywords are the following in english-singular / english-plural / spanish-singular / spanish-plural:
breakfast / breakfasts / desayuno / desayunos
brunch / brunches / alumerzo-desayuno / almuerzo-desayunos
lunch / lunches / almuerzo / almuerzos
dinner / dinners / cena / cenas
So... I can imagine the following tests cases:
[
'language' => 'en-US',
'parameters' => [
'breakfast' => 4,
'dinner' => 1,
],
'expectedOutput' => 'Included: 4 breakfasts and 1 dinner.'
],
[
'language' => 'es-ES',
'parameters' => [
'breakfast' => 2,
'brunch' => 1,
'lunch' => 5,
'dinner' => 2,
],
'expectedOutput' => 'Incluido: 2 desayunos, 1 desayuno-almuerzo, 5 almuerzos y 2 cenas.'
],
[
'language' => 'en-US',
'parameters' => [
'breakfast' => 1,
],
'expectedOutput' => 'Included: 1 breakfast.'
],
[
'language' => 'es-ES',
'parameters' => [
'dinner' => 4,
'lunch' => 3,
],
'expectedOutput' => 'Incluido: 3 almuerzos y 4 cenas.'
],
What the translation engine can do to some extent is use a number to switch between different cases (for singular/plural). Have a look at transChoice
for symfony 3.4 (til 4.1.x apparently). Since symfony 4.2, the ICU message format (follow links too) may be used to handle pluralization.
However, (conditional) string building is not really a task for the translation engine at all (even though for simple cases transChoice or the ICU message format might work out fine). So you'll probably end up building the strings from bits and pieces.
// fetch translations for the itemss
$items = [];
foreach($parameters as $key => $count) {
if($count <= 0) {
continue;
}
$items[] = $translator->transChoice($key, $count);
}
// handle 2+ items differently from 1 item
if(count($items) > 1) {
// get last item
$last = array_pop($items);
// comma-separate all other items, add last item with "and"
$itemstring = implode(', ', $items)
. $translator->trans('included_and')
. $last;
} else {
$itemstring = $items[0] ?? ''; // if no items or something like
// $translator->trans('included_none')
}
return $translator->trans('included', ['%items%' => $itemstring]);
and in the translation file (english):
'included' => 'Included: %items%.',
'included_and' => ' and ',
'breakfast' => '%count% breakfast|%count% breakfasts', // etc. for all other stuff
Please note, that transChoice
should automatically set the %count%
placeholder to the count provided as the second parameter to transChoice
.
You could easily split the translatable strings into breakfast_singular
and breakfast_plural
if you wanted to avoid the use of transChoice
, you would have to replace
transChoice($key, $count)
with
trans($key.($count > 1 ? '_plural' : '_singular'), ['%count%' => $count])
You could also expand the included
string into included_single
and included_multiple
where the latter is being translated as 'Included: %items% and %last%'
, with appropriate parameters and conditionals.
Ultimately though, string generation or more specifically language generation isn't really a job for a simple translation engine.