src/CoreBundle/Command/UpdateVueTranslations.php
<?php
declare(strict_types=1);
/* For licensing terms, see /license.txt */
namespace Chamilo\CoreBundle\Command;
use Chamilo\CoreBundle\Entity\Language;
use Chamilo\CoreBundle\Repository\LanguageRepository;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use const JSON_PRETTY_PRINT;
use const JSON_UNESCAPED_SLASHES;
#[AsCommand(
name: 'chamilo:update_vue_translations',
description: 'This command updates the json vue locale files based in the Symfony /translation folder',
)]
class UpdateVueTranslations extends Command
{
private LanguageRepository $languageRepository;
private ParameterBagInterface $parameterBag;
private TranslatorInterface $translator;
public function __construct(LanguageRepository $languageRepository, ParameterBagInterface $parameterBag, TranslatorInterface $translator)
{
$this->languageRepository = $languageRepository;
$this->parameterBag = $parameterBag;
$this->translator = $translator;
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$languages = $this->languageRepository->findAll();
$dir = $this->parameterBag->get('kernel.project_dir');
$vueLocalePath = $dir.'/assets/locales/';
$englishJson = file_get_contents($vueLocalePath.'en.json');
$translations = json_decode($englishJson, true);
foreach ($languages as $language) {
$iso = $language->getIsocode();
if ('en_US' === $iso) {
// Only update with the same variables.
$newLanguage = [];
foreach ($translations as $variable => $translation) {
$translated = $this->getTranslationWithFallback($variable, $language);
$newLanguage[$variable] = $this->replaceMarkers($translated);
}
$newLanguageToString = json_encode($newLanguage, JSON_PRETTY_PRINT);
$fileToSave = $vueLocalePath.'en.json';
file_put_contents($fileToSave, $newLanguageToString);
continue;
}
$newLanguage = [];
foreach ($translations as $variable => $translation) {
// $translated = $this->translator->trans($variable, [], null, $iso);
$translated = $this->getTranslationWithFallback($variable, $language);
$newLanguage[$variable] = $this->replaceMarkers($translated);
}
$newLanguage = array_filter($newLanguage);
$newLanguageToString = json_encode($newLanguage, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$newLanguageToString = str_replace('</br>', '<br>', $newLanguageToString);
$fileToSave = $vueLocalePath.$iso.'.json';
file_put_contents($fileToSave, $newLanguageToString);
$output->writeln("json file generated for iso $iso: $fileToSave");
}
$output->writeln('');
$output->writeln("Now you can commit the changes in $vueLocalePath ");
return Command::SUCCESS;
}
/**
* Gets the translation for a given variable with fallbacks to parent language and base language.
*
* @param string $variable the variable to be translated
* @param Language $language the Language entity for the current language
*
* @return string the translated string
*/
private function getTranslationWithFallback(string $variable, Language $language): string
{
// Get the ISO code of the current language
$iso = $language->getIsocode();
// Try to translate the variable in the current language
$translated = $this->translator->trans($variable, [], 'messages', $iso);
// Check if the translation is not found and if there is a parent language
if ($translated === $variable) {
if ($language->getParent()) {
// Get the parent language entity and its ISO code
$parentLanguage = $language->getParent();
$parentIso = $parentLanguage->getIsocode();
// Try to translate the variable in the parent language
$translated = $this->translator->trans($variable, [], 'messages', $parentIso);
// Check if translation is still not found and use the base language (English)
if ($translated === $variable) {
$translated = $this->translator->trans($variable, [], 'messages', 'en');
}
} else {
$translated = $this->translator->trans($variable, [], 'messages', 'en');
}
}
return $translated;
}
/**
* Replace specifiers in a string to allow rendering them by i18n.
*
* <code>
* $txt = "Bonjour %s. Je m’appelle %s";
* $replaced = replaceMarkers($txt); // Bonjour {0}. Je m’appelle {1}
* </code>
*/
private function replaceMarkers(string $text): string
{
$count = 0;
$replace = function ($matches) use (&$count) {
$type = $matches[1];
return match ($type) {
's', 'd', 'f' => '{'.$count++.'}',
default => $matches[0],
};
};
$pattern = '/%([sdf])/';
return preg_replace_callback($pattern, $replace, $text);
}
}