Skip to content

Commit

Permalink
[FEATURE] Xlf2csv
Browse files Browse the repository at this point in the history
  • Loading branch information
georgringer committed Jul 15, 2024
1 parent 41c7f10 commit 1d84135
Show file tree
Hide file tree
Showing 22 changed files with 259 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class ConvertCommand extends Command
class ConvertCsv2XlfCommand extends Command
{
protected function configure(): void
{
Expand Down Expand Up @@ -52,7 +52,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
}

$convertService = GeneralUtility::makeInstance(ConvertService::class);
$statistic = $convertService->convert($in, $out, $rebuild);
$statistic = $convertService->convertCsv2Xlf($in, $out, $rebuild);

$io->table(['Language', 'Count'], array_map(fn ($k, $v) => [$k, $v], array_keys($statistic), $statistic));

Expand Down
52 changes: 52 additions & 0 deletions Classes/Command/ConvertXlf2CsvCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace StudioMitte\Csv2Xlf\Command;

use StudioMitte\Csv2Xlf\Service\ConvertService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class ConvertXlf2CsvCommand extends Command
{
protected function configure(): void
{
$this->addArgument('in', InputArgument::REQUIRED, 'XLF file to convert');
$this->addArgument('out', InputArgument::REQUIRED, 'Path to save the CSV file');
$this->addArgument('languages', InputArgument::REQUIRED, 'Comma separated list of languages');
}

protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$io->title($this->getDescription());

$in = $input->getArgument('in') ?? '';
$out = $input->getArgument('out') ?? '';
$languages = GeneralUtility::trimExplode(',', $input->getArgument('languages') ?? '', true);

if (!str_starts_with('/', $in)) {
$in = Environment::getProjectPath() . '/' . $in;
}

if (!is_file($in)) {
$io->error(sprintf('File "%s" could not be found', $in));
return Command::FAILURE;
}
if (empty($languages)) {
$io->error('No languages provided');
return Command::FAILURE;
}

$convertService = GeneralUtility::makeInstance(ConvertService::class);
$statistic = $convertService->convertXlf2Csv($in, $out, $languages);

return Command::SUCCESS;
}
}
53 changes: 47 additions & 6 deletions Classes/Service/ConvertService.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,53 @@ class ConvertService
{
public function __construct(
protected readonly XlfFileService $xlfFileService,
protected readonly CsvReader $csvReader
protected readonly CsvService $csvService
) {}

public function convert(string $csvFilePath, string $out, bool $forceRebuild): array
public function convertXlf2Csv(string $xlfFilePath, string $out, array $languages): array
{
$headers = ['key', 'en'];
$defaultLabels = $this->xlfFileService->getLabels($xlfFilePath, 'en');
$translations = [];
foreach ($languages as $language) {
if ($language === 'en') {
throw new \UnexpectedValueException('Language "en" is not allowed', 1721035547);
}
$headers[] = $language;
$path = $this->getXlfFileNameForLanguage($xlfFilePath, $language);
if (!is_file($path)) {
continue;
}
$translations[$language] = $this->xlfFileService->getLabels($path, $language);
}

$flatList = [];

foreach ($defaultLabels as $label) {
$flatList[$label->key]['key'] = $label->key;
$flatList[$label->key]['en'] = $label->source;

foreach ($languages as $language) {
if (isset($translations[$language][$label->key])) {
$flatList[$label->key][$language] = $translations[$language][$label->key]->translation;
} else {
$flatList[$label->key][$language] = '';
}
}
}

$this->csvService->generateCsv($out, $flatList, $headers);
return $flatList;
}

public function convertCsv2Xlf(string $csvFilePath, string $out, bool $forceRebuild): array
{
$stats = [];
$data = $this->csvReader->getFromFile($csvFilePath);
$data = $this->csvService->getFromFile($csvFilePath);

foreach ($data as $language => $labels) {
if ($language !== 'en') {
$fileInfo = pathinfo($out);

$targetFilename = sprintf('%s/%s.%s', $fileInfo['dirname'], $language, $fileInfo['basename']);
$targetFilename = $this->getXlfFileNameForLanguage($out, $language);
} else {
$targetFilename = $out;
}
Expand Down Expand Up @@ -53,4 +87,11 @@ protected function addExistingLabels(array $labels, string $language, string $pa

return array_merge($existingLabels, $labels);
}

protected function getXlfFileNameForLanguage(string $out, string $language): string
{
$fileInfo = pathinfo($out);

return sprintf('%s/%s.%s', $fileInfo['dirname'], $language, $fileInfo['basename']);
}
}
18 changes: 13 additions & 5 deletions Classes/Service/CsvReader.php → Classes/Service/CsvService.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@
namespace StudioMitte\Csv2Xlf\Service;

use League\Csv\Reader;
use League\Csv\Writer;
use StudioMitte\Csv2Xlf\Domain\Model\Dto\Label;

class CsvReader
class CsvService
{
public function getFromFile(string $filePath): array
{
$csvReader = Reader::createFromPath($filePath, 'r');
$csvReader->setHeaderOffset(0);
$reader = Reader::createFromPath($filePath, 'r');
$reader->setHeaderOffset(0);

$header = $csvReader->getHeader();
$header = $reader->getHeader();
if ($header[0] !== 'key') {
throw new \RuntimeException('CSV file has no "key" column on 1st position', 1719919250);
}
Expand All @@ -23,7 +24,7 @@ public function getFromFile(string $filePath): array
}

$labels = [];
foreach ($csvReader->getRecords() as $row) {
foreach ($reader->getRecords() as $row) {
$key = $row['key'];
$default = $row['en'];
unset($row['key'], $row['en']);
Expand All @@ -40,4 +41,11 @@ public function getFromFile(string $filePath): array

return $labels;
}

public function generateCsv(string $path, array $data, array $headers)
{
$writer = Writer::createFromPath($path, 'w');
$writer->insertOne($headers);
$writer->insertAll($data);
}
}
3 changes: 3 additions & 0 deletions Classes/Service/XlfFileService.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@

class XlfFileService
{
/**
* @return Label[]
*/
public function getLabels(string $path, string $language): array
{
$xmlContent = file_get_contents($path);
Expand Down
11 changes: 9 additions & 2 deletions Configuration/Services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ services:
StudioMitte\Csv2Xlf\:
resource: '../Classes/*'

StudioMitte\Csv2Xlf\Command\ConvertCommand:
StudioMitte\Csv2Xlf\Command\ConvertCsv2XlfCommand:
tags:
- name: 'console.command'
command: 'csv2xlf:convert'
command: 'csv2xlf:csv2xlf'
description: 'Convert CSV to XLF'
schedulable: true

StudioMitte\Csv2Xlf\Command\ConvertXlf2CsvCommand:
tags:
- name: 'console.command'
command: 'csv2xlf:xlf2csv'
description: 'Convert XLF to CSV'
schedulable: true

StudioMitte\Csv2Xlf\Service\ConvertService:
public: true

Expand Down
11 changes: 9 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ composer require studiomitte/csv2xlf

## Usage

### CSV to XLF

CSV looks like this
```csv
"key","en","de"
Expand All @@ -24,14 +26,19 @@ CSV looks like this

With the following requirements:
- The first row is the header
- The header starts with `key`, followed by `en` and afterwards the language codes
- The header starts with `key`, followed by `en` and afterward the language codes
- Default is always `en`


```bash
./bin/typo3 csv2xlf:convert packages/csv2xlf/Resources/Private/Examples/in.csv packages/csv2xlf/Resources/Private/Examples/out.xlf
./bin/typo3 csv2xlf:csv2xlf packages/csv2xlf/Resources/Private/Examples/csv2xlf/in.csv packages/csv2xlf/Resources/Private/Examples/csv2xlf/out.xlf
```

### XLF to CSV

```bash
./bin/typo3 csv2xlf:xlf2csv packages/csv2xlf/Resources/Private/Examples/xlf2csv/in.xlf packages/csv2xlf/Resources/Private/Examples/xlf2csv/out.csv de,fr,es
```

## Credits

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
16 changes: 16 additions & 0 deletions Resources/Private/Examples/xlf2csv/de.in.xlf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff version="1.2" xmlns:t3="http://typo3.org/schemas/xliff" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" target-language="de" datatype="plaintext" original="messages">
<header/>
<body>
<trans-unit id="example" resname="example" approved="yes">
<source>This is an example NEW</source>
<target>Das ist ein Beispiel</target>
</trans-unit>
<trans-unit id="privacy" resname="privacy" approved="yes">
<source>&lt;![CDATA[&lt;h3&gt;Privacy&lt;/h3&gt;</source>
<target>&lt;![CDATA[&lt;h3&gt;Datenschutzhinweis (bs)&lt;/h3&gt;</target>
</trans-unit>
</body>
</file>
</xliff>
16 changes: 16 additions & 0 deletions Resources/Private/Examples/xlf2csv/fr.in.xlf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff version="1.2" xmlns:t3="http://typo3.org/schemas/xliff" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" target-language="fr" datatype="plaintext" original="messages">
<header/>
<body>
<trans-unit id="example" resname="example" approved="yes">
<source>This is an example</source>
<target>Ceci est un exemple</target>
</trans-unit>
<trans-unit id="privacy" resname="privacy" approved="yes">
<source>&lt;![CDATA[&lt;h3&gt;Datenschutzhinweis&lt;/h3&gt;</source>
<target>&lt;![CDATA[&lt;h3&gt;Protection des données&lt;/h3&gt;</target>
</trans-unit>
</body>
</file>
</xliff>
14 changes: 14 additions & 0 deletions Resources/Private/Examples/xlf2csv/in.xlf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff version="1.2" xmlns:t3="http://typo3.org/schemas/xliff" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="messages">
<header/>
<body>
<trans-unit id="example" resname="example">
<source>This is an example NEW</source>
</trans-unit>
<trans-unit id="privacy" resname="privacy">
<source>&lt;![CDATA[&lt;h3&gt;Price&lt;/h3&gt;</source>
</trans-unit>
</body>
</file>
</xliff>
3 changes: 3 additions & 0 deletions Resources/Private/Examples/xlf2csv/out.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
key,en,de,fr
example,"This is an example NEW","Das ist ein Beispiel","Ceci est un exemple"
privacy,<![CDATA[<h3>Price</h3>,"<![CDATA[<h3>Datenschutzhinweis (bs)</h3>","<![CDATA[<h3>Protection des données</h3>"
23 changes: 18 additions & 5 deletions Tests/Unit/Service/ConvertServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,37 @@
namespace StudioMitte\Csv2Xlf\Tests\Service;

use StudioMitte\Csv2Xlf\Service\ConvertService;
use StudioMitte\Csv2Xlf\Service\CsvReader;
use StudioMitte\Csv2Xlf\Service\CsvService;
use StudioMitte\Csv2Xlf\Service\XlfFileService;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase;

class ConvertServiceTest extends UnitTestCase
{
public function testConversion(): void
public function testConversionCsv2Xlf(): void
{
$exampleFile = __DIR__ . '/Fixtures/example.csv';
$out = __DIR__ . '/Result/result.xlf';
$forceRebuild = true;

$xlfService = new XlfFileService();
$csvReaderService = new CsvReader();
$convertService = new ConvertService($xlfService, $csvReaderService);
$convertService->convert($exampleFile, $out, $forceRebuild);
$csvService = new CsvService();
$convertService = new ConvertService($xlfService, $csvService);
$convertService->convertCsv2Xlf($exampleFile, $out, $forceRebuild);

self::assertFileEquals(__DIR__ . '/Fixtures/result.xlf', $out, 'Conversion failed for default');
self::assertFileEquals(__DIR__ . '/Fixtures/de.result.xlf', __DIR__ . '/Result/de.result.xlf', 'Conversion failed for de');
}

public function testConversionXlf2Csv(): void
{
$exampleFile = __DIR__ . '/Fixtures/xlf2csv/in.xlf';
$out = __DIR__ . '/Result/result.csv';

$xlfService = new XlfFileService();
$csvService = new CsvService();
$convertService = new ConvertService($xlfService, $csvService);
$convertService->convertXlf2Csv($exampleFile, $out, ['de', 'fr']);

self::assertFileEquals(__DIR__ . '/Fixtures/xlf2csv/result.csv', $out, 'Conversion failed for default');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

namespace StudioMitte\Csv2Xlf\Tests\Service;

use StudioMitte\Csv2Xlf\Service\CsvReader;
use StudioMitte\Csv2Xlf\Service\CsvService;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase;

class CsvReaderTest extends UnitTestCase
class CsvServiceTest extends UnitTestCase
{
protected CsvReader $subject;
protected CsvService $subject;

public function setUp(): void
{
$this->subject = new CsvReader();
$this->subject = new CsvService();
parent::setUp();
}

Expand Down
16 changes: 16 additions & 0 deletions Tests/Unit/Service/Fixtures/xlf2csv/de.in.xlf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<xliff version="1.2" xmlns:t3="http://typo3.org/schemas/xliff" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" target-language="de" datatype="plaintext" original="messages">
<header/>
<body>
<trans-unit id="example" resname="example" approved="yes">
<source>This is an example NEW</source>
<target>Das ist ein Beispiel</target>
</trans-unit>
<trans-unit id="privacy" resname="privacy" approved="yes">
<source>&lt;![CDATA[&lt;h3&gt;Privacy&lt;/h3&gt;</source>
<target>&lt;![CDATA[&lt;h3&gt;Datenschutzhinweis (bs)&lt;/h3&gt;</target>
</trans-unit>
</body>
</file>
</xliff>
Loading

0 comments on commit 1d84135

Please sign in to comment.