10000 [Translation][FrameworkBundle] Adding Translation Providers by welcoMattic · Pull Request #37462 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Translation][FrameworkBundle] Adding Translation Providers #37462

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
WIP Crowdin API Client
  • Loading branch information
welcoMattic committed Sep 28, 2020
commit 5f866c5862c296a207a0bdb97abaa802813d354f
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,9 @@
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Component\String\LazyString;
use Symfony\Component\S 8000 tring\Slugger\SluggerInterface;
use Symfony\Component\Translation\Bridge\Crowdin\CrowdinProviderFactory;
use Symfony\Component\Translation\Bridge\Loco\LocoProviderFactory;
use Symfony\Component\Translation\Bridge\Phrase\PhraseProviderFactory;
use Symfony\Component\Translation\Command\XliffLintCommand as BaseXliffLintCommand;
use Symfony\Component\Translation\PseudoLocalizationTranslator;
use Symfony\Component\Translation\Translator;
Expand Down Expand Up @@ -1324,10 +1326,12 @@ private function registerTranslatorConfiguration(array $config, ContainerBuilder
->replaceArgument(1, $config['enabled_locales'])
;

$container->getDefinition('TranslationProviders')->setArgument(0, $config['providers']);
$container->getDefinition('translation.providers')->setArgument(0, $config['providers']);

$classToServices = [
LocoProviderFactory::class => 'translation.provider_factory.loco',
CrowdinProviderFactory::class => 'translation.provider_factory.crowdin',
PhraseProviderFactory::class => 'translation.provider_factory.phrase',
];

foreach ($classToServices as $class => $service) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@

->set('console.command.translation_pull', TranslationPullCommand::class)
->args([
service('TranslationProviders'),
service('translation.providers'),
service('translation.writer'),
service('translation.reader'),
param('kernel.default_locale'),
Expand All @@ -248,7 +248,7 @@

->set('console.command.translation_push', TranslationPushCommand::class)
->args([
service('TranslationProviders'),
service('translation.providers'),
service('translation.reader'),
param('translator.default_path'),
[], // Translator paths
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Component\Translation\Bridge\Crowdin\CrowdinProviderFactory;
use Symfony\Component\Translation\Bridge\Phrase\PhraseProviderFactory;
use Symfony\Component\Translation\Bridge\Loco\LocoProviderFactory;
use Symfony\Component\Translation\Provider\AbstractProviderFactory;

Expand Down Expand Up @@ -39,5 +40,12 @@
])
->parent('translation.provider_factory.abstract')
->tag('translation.provider_factory')

->set('translation.provider_factory.phrase', PhraseProviderFactory::class)
->args([
service('translator.data_collector')->nullOnInvalid(),
])
->parent('translation.provider_factory.abstract')
->tag('translation.provider_factory')
;
};
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,22 @@
* @experimental in 5.2
*
* In Crowdin:
* Source strings refers D7AF to Symfony's translation keys
*/
final class CrowdinProvider extends AbstractProvider
{
protected const HOST = 'api.crowdin.com';
protected const HOST = 'crowdin.com/api/v2';

private $apiKey;
private $projectId;
private $token;
private $loader;
private $logger;
private $defaultLocale;

public function __construct(string $apiKey, HttpClientInterface $client = null, LoaderInterface $loader = null, LoggerInterface $logger = null, string $defaultLocale = null)
public function __construct(string $projectId, string $token, HttpClientInterface $client = null, LoaderInterface $loader = null, LoggerInterface $logger = null, string $defaultLocale = null)
{
$this->apiKey = $apiKey;
$this->projectId = $projectId;
$this->token = $token;
$this->loader = $loader;
$this->logger = $logger;
$this->defaultLocale = $defaultLocale;
Expand All @@ -52,18 +55,30 @@ public function __toString(): string

public function write(TranslatorBag $translations, bool $override = false): void
{
// TODO: Implement write() method.
foreach ($translations->getCatalogues() as $catalogue) {
foreach ($catalogue->all() as $domain => $messages) {
$locale = $catalogue->getLocale();

// check if domain exists, if not, create it

foreach ($messages as $id => $message) {
$this->addString($id);
$this->addTranslation($id, $message, $locale);
}
}
}
}

/**
* @see https://support.crowdin.com/api/v2/#operation/api.projects.translations.exports.post
*/
public function read(array $domains, array $locales): TranslatorBag
{
$filter = $domains ? implode(',', $domains) : '*';
$translatorBag = new TranslatorBag();

foreach ($locales as $locale) {
$response = $this->client->request('GET', sprintf('https://%s/api/export/locale/%s.xlf?filter=%s', $this->getEndpoint(), $locale, $filter), [
'headers' => $this->getDefaultHeaders(),
]);
$fileId = $this->getFileId();

$responseContent = $response->getContent(false);

Expand All @@ -83,4 +98,69 @@ public function delete(TranslatorBag $translations): void
{
// TODO: Implement delete() method.
}

protected function getDefaultHeaders(): array
{
return [
'Authorization' => 'Bearer ' . $this->token,
];
}

/**
* This function allows creation of a new translation key.
*
* @see https://support.crowdin.com/api/v2/#operation/api.projects.strings.post
*/
private function addString(string $id): void
{
$response = $this->client->request('POST', sprintf('https://%s/projects/%s/strings', $this->getEndpoint(), $this->projectId), [
'headers' => $this->getDefaultHeaders(),
'body' => [
'text' => $id,
'identifier' => $id,
],
]);

if (Response::HTTP_CONFLICT === $response->getStatusCode()) {
$this->logger->warning(sprintf('Translation key (%s) already exists in Crowdin.', $id), [
'id' => $id,
]);
} elseif (Response::HTTP_CREATED !== $response->getStatusCode()) {
throw new TransportException(sprintf('Unable to add new translation key (%s) to Crowdin: (status code: "%s") "%s".', $id, $response->getStatusCode(), $response->getContent(false)), $response);
}
}

/**
* This function allows translation of a message.
*
* @see https://support.crowdin.com/api/v2/#operation/api.projects.translations.post
*/
private function addTranslation(string $id, string $message, string $locale): void
{
$response = $this->client->request('POST', sprintf('https://%s/projects/%s/translations', $this->getEndpoint(), $this->projectId), [
'headers' => $this->getDefaultHeaders(),
'body' => [
'stringId' => $id,
'languageId' => $locale,
'text' => $message,
],
]);

if (Response::HTTP_CREATED !== $response->getStatusCode()) {
throw new TransportException(sprintf('Unable to add new translation message "%s" (for key: "%s") to Crowdin: (status code: "%s") "%s".', $message, $id, $response->getStatusCode(), $response->getContent(false)), $response);
}
}

/**
* @todo: Not sure at all of this
*/
private function getFileId(): int
{
$response = $this->client->request('GET', sprintf('https://%s/projects/%s/files', $this->getEndpoint(), $this->projectId), [
'headers' => $this->getDefaultHeaders(),
]);
$files = json_decode($response->getContent());

return $files->data[0]->data->id;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ final class CrowdinProviderFactory extends AbstractProviderFactory
public function create(Dsn $dsn): ProviderInterface
{
if ('crowdin' === $dsn->getScheme()) {
return (new CrowdinProvider($this->getUser($dsn), $this->client, $this->loader, $this->logger, $this->defaultLocale))
return (new CrowdinProvider($this->getUser($dsn), $this->getPassword($dsn), $this->client, $this->loader, $this->logger, $this->defaultLocale))
->setHost('default' === $dsn->getHost() ? null : $dsn->getHost())
->setPort($dsn->getPort())
;
Expand Down
25 changes: 14 additions & 11 deletions src/Symfony/Component/Translation/Bridge/Loco/LocoProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
* In Loco:
* tags refers to Symfony's translation domains
* assets refers to Symfony's translation keys
* translations refers to Symfony's translation messages
* translations refers to Symfony's translated messages
*/
final class LocoProvider extends AbstractProvider
{
protected const HOST = 'localise.biz';
protected const HOST = 'localise.biz/api';

/** @var string */
private $apiKey;
Expand Down Expand Up @@ -92,7 +92,7 @@ public function read(array $domains, array $locales): TranslatorBag
$translatorBag = new TranslatorBag();

foreach ($locales as $local 10000 e) {
$response = $this->client->request('GET', sprintf('https://%s/api/export/locale/%s.xlf?filter=%s', $this->getEndpoint(), $locale, $filter), [
$response = $this->client->request('GET', sprintf('https://%s/export/locale/%s.xlf?filter=%s', $this->getEndpoint(), $locale, $filter), [
'headers' => $this->getDefaultHeaders(),
]);

Expand Down Expand Up @@ -135,13 +135,16 @@ public function delete(TranslatorBag $translations): void
protected function getDefaultHeaders(): array
{
return [
'Authorization' => 'Loco '.$this->apiKey,
'Authorization' => 'Loco ' . $this->apiKey,
];
}

/**
* This function allows creation of a new translation key.
*/
private function createAsset(string $id): void
{
$response = $this->client->request('POST', sprintf('https://%s/api/assets', $this->getEndpoint()), [
$response = $this->client->request('POST', sprintf('https://%s/assets', $this->getEndpoint()), [
'headers' => $this->getDefaultHeaders(),
'body' => [
'name' => $id,
Expand All @@ -162,13 +165,13 @@ private function createAsset(string $id): void

private function translateAsset(string $id, string $message, string $locale): void
{
$response = $this->client->request('POST', sprintf('https://%s/api/translations/%s/%s', $this->getEndpoint(), $id, $locale), [
$response = $this->client->request('POST', sprintf('https://%s/translations/%s/%s', $this->getEndpoint(), $id, $locale), [
'headers' => $this->getDefaultHeaders(),
'body' => $message,
]);

if (Response::HTTP_OK !== $response->getStatusCode()) {
throw new TransportException(sprintf('Unable to add translation message (for key: "%s") to Loco: "%s".', $id, $response->getContent(false)), $response);
throw new TransportException(sprintf('Unable to add translation message "%s" (for key: "%s") to Loco: "%s".', $message, $id, $response->getContent(false)), $response);
}
}

Expand All @@ -180,7 +183,7 @@ private function tagsAssets(array $ids, string $tag): void
$this->createTag($tag);
}

$response = $this->client->request('POST', sprintf('https://%s/api/tags/%s.json', $this->getEndpoint(), $tag), [
$response = $this->client->request('POST', sprintf('https://%s/tags/%s.json', $this->getEndpoint(), $tag), [
'headers' => $this->getDefaultHeaders(),
'body' => $idsAsString,
]);
Expand All @@ -192,7 +195,7 @@ private function tagsAssets(array $ids, string $tag): void

private function createTag(string $tag): void
{
$response = $this->client->request('POST', sprintf('https://%s/api/tags.json', $this->getEndpoint()), [
$response = $this->client->request('POST', sprintf('https://%s/tags.json', $this->getEndpoint()), [
'headers' => $this->getDefaultHeaders(),
'body' => [
'name' => $tag,
Expand All @@ -206,7 +209,7 @@ private function createTag(string $tag): void

private function getTags(): array
{
$response = $this->client->request('GET', sprintf('https://%s/api/tags.json', $this->getEndpoint()), [
$response = $this->client->request('GET', sprintf('https://%s/tags.json', $this->getEndpoint()), [
'headers' => $this->getDefaultHeaders(),
]);

Expand All @@ -221,7 +224,7 @@ private function getTags(): array

private function deleteAsset(string $id): void
{
$response = $this->client->request('DELETE', sprintf('https://%s/api/assets/%s.json', $this->getEndpoint(), $id), [
$response = $this->client->request('DELETE', sprintf('https://%s/assets/%s.json', $this->getEndpoint(), $id), [
'headers' => $this->getDefaultHeaders(),
]);

Expand Down
67 changes: 67 additions & 0 deletions src/Symfony/Component/Translation/Bridge/Phrase/PhraseProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Translation\Bridge\Phrase;

use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Translation\Exception\TransportException;
use Symfony\Component\Translation\Loader\LoaderInterface;
use Symfony\Component\Translation\Provider\AbstractProvider;
use Symfony\Component\Translation\TranslatorBag;
use Symfony\Contracts\HttpClient\HttpClientInterface;

/**
* @author Fabien Potencier <fabien@symfony.com>
*
* @experimental in 5.2
*
* In Phrase:
*/
final class PhraseProvider extends AbstractProvider
{
protected const HOST = 'api.phrase.com/v2';

private $apiKey;
private $loader;
private $logger;
private $defaultLocale;

public function __construct(string $apiKey, HttpClientInterface $client = null, LoaderInterface $loader = null, LoggerInterface $logger = null, string $defaultLocale = null)
{
$this->apiKey = $apiKey;
$this->loader = $loader;
$this->logger = $logger;
$this->defaultLocale = $defaultLocale;

parent::__construct($client);
}

public function __toString(): string
{
return sprintf('phrase://%s', $this->getEndpoint());
}

public function write(TranslatorBag $translations, bool $override = false): void
{
// TODO: Implement write() method.
}

public function read(array $domains, array $locales): TranslatorBag
{
// TODO: Implement read() method.
}

public function delete(TranslatorBag $translations): void
{
// TODO: Implement delete() method.
}
}
Loading
0