Convertir vos fichiers de traduction Symfony (French)

2013-09-10

  symfony    french    internationalization 

Je vous propose dans cet article une commande Symfony permettant de convertir vos fichiers de traduction d'un format à un autre.

Celle-ci peut également être utilisée pour créer des fichiers de traduction depuis des fichiers CSV par exemple, facilement exportables depuis une matrice de contenu Excel, souvent fournies par les clients.

La commande Symfony

Voici la commande Symfony que j'ai écrite pour exécuter cette tâche. Celle-ci se base sur les Loader et Dumper des différents formats du composant Translation


namespace Eko\\MiscBundle\\Command;

use Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand;

use Symfony\\Component\\Console\\Input\\InputOption;
use Symfony\\Component\\Console\\Input\\InputInterface;
use Symfony\\Component\\Console\\Output\\OutputInterface;
use Symfony\\Component\\Filesystem\\Filesystem;
use Symfony\\Component\\Finder\\Finder;
use Symfony\\Component\\Translation\\Writer\\TranslationWriter;

/**
 * Translation import command
 *
 * @author Vincent Composieux 
 */
class TranslationImportCommand extends ContainerAwareCommand
{
    /**
     * @var Finder
     */
    protected $finder;

    /**
     * @var Filesystem
     */
    protected $filesystem;

    /**
     * {@inheritDoc}
     */
    protected function configure()
    {
        parent::configure();

        $this
            ->setName('eko:translation:convert')
            ->setDescription('Translation convert command from an input format to another format')
            ->setHelp('You must specify a path using the --path option.')
            ->addOption('path', null, InputOption::VALUE_REQUIRED, 'Specify a path of files')
            ->addOption('input', null, InputOption::VALUE_REQUIRED, 'Specifiy a input translation format')
            ->addOption('output', null, InputOption::VALUE_OPTIONAL, 'Specifiy an output translation format (default: xliff)')
        ;
    }

    /**
     * {@inheritDoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $path = $input->getOption('path');
        $inputFormat = $input->getOption('input');
        $outputFormat = $input->getOption('output') ?: 'xliff';

        if (!$inputFormat) {
            throw new \\InvalidArgumentException('You must specify a --input format option.');
        }

        if (!$path || !$this->getFilesystem()->exists($path)) {
            throw new \\InvalidArgumentException('You must specify a valid --path option.');
        }

        $dumper = $this->getDumper($outputFormat);
        $this->getTranslationWriter()->addDumper($outputFormat, $dumper);

        $files = $this->getFinder()->files()->name('/[a-z]+\\.[a-z]{2}\\.[a-z]+/')->in($path);

        foreach ($files as $file) {
            list($domain, $language) = explode('.', $file->getFilename());

            $output->writeln(sprintf('Starts importing file %s', $file->getRealPath()));

            $file = $this->getLoader($inputFormat)->load($file->getRealPath(), $language);
            $messages = $file->all($domain);

            if (!$messages) {
                $output->writeln('No translations found in this file.');

                continue;
            }

            try {
                $this->getTranslationWriter()->writeTranslations($file, $outputFormat, array(
                    'path' => $this->getTranslationPath())
                );

                $output->writeln('? Translation file saved.');
            } catch (\\Exception $e) {
                $output->writeln(sprintf('An error has occured while trying to write translations: %s', $e->getMessage()));
            }
        }
    }

    /**
     * Returns Symfony Filesystem component
     *
     * @return Filesystem
     */
    protected function getFilesystem()
    {
        if (null === $this->filesystem) {
            $this->filesystem = new Filesystem();
        }

        return $this->filesystem;
    }

    /**
     * Returns Symfony Finder component
     *
     * @return Finder
     */
    protected function getFinder()
    {
        if (null === $this->finder) {
            $this->finder = new Finder();
        }

        return $this->finder;
    }

    /**
     * Returns Symfony translation writer service
     *
     * @return TranslationWriter
     */
    protected function getTranslationWriter()
    {
        return $this->getContainer()->get('translation.writer');
    }

    /**
     * Returns Symfony requested format loader
     *
     * @param string $format
     *
     * @return \\Symfony\\Component\\Translation\\Loader\\LoaderInterface
     *
     * @throws \\InvalidArgumentException
     */
    protected function getLoader($format)
    {
        $service = sprintf('translation.loader.%s', $format);

        if (!$this->getContainer()->has($service)) {
            throw new \\InvalidArgumentException(sprintf('Unable to find Symfony Translation loader for format "%s"', $format));
        }

        return $this->getContainer()->get($service);
    }

    /**
     * Returns Symfony requested format dumper
     *
     * @param string $format
     *
     * @return \\Symfony\\Component\\Translation\\Dumper\\DumperInterface
     *
     * @throws \\InvalidArgumentException
     */
    protected function getDumper($format)
    {
        $service = sprintf('translation.dumper.%s', $format);

        if (!$this->getContainer()->has($service)) {
            throw new \\InvalidArgumentException(sprintf('Unable to find Symfony Translation dumper for format "%s"', $format));
        }

        return $this->getContainer()->get($service);
    }

    /**
     * Returns translation path
     *
     * @return string
     */
    protected function getTranslationPath()
    {
        return $this->getContainer()->get('kernel')->getRootDir() . '/Resources/translations';
    }
}

Cette commande plutôt simple à comprendre utilise également les composants Finder et Filesystem pour rechercher les fichiers du répertoire donné et vérifier les différents répertoires donnés.

Voyons maintenant comment utiliser cette commande.

Préparation de vos fichiers

Nous allons créer des fichiers nommés messages.[lang].csv dans un répertoire, tel que suit :


|-- var
|   |-- www
|       |-- translations
|   |       |-- messages.de.csv
|   |       |-- messages.en.csv
|   |       |-- messages.fr.csv

Exécution de la commande

Exécutons donc notre commande afin d'insérer ces fichiers .csv au format .xliff dans notre projet Symfony:


$ php app/console eko:translation:convert --path=/var/www/translations --input=csv --output=xliff
Starts importing file /var/www/translations/messages.de.csv
→ Translation file saved.
Starts importing file /var/www/translations/messages.en.csv
→ Translation file saved.
Starts importing file /var/www/translations/messages.fr.csv
→ Translation file saved.

Et voilà, vos traductions sont maintenant disponibles dans le répertoire app/Resources/translations de votre projet dans le format demandé dans l'option output.

J'espère que cette commande pourra être utile à certains.

Comments