[Translation] improve CrowdinProvider

This commit is contained in:
Nicolas Grekas 2021-05-09 18:42:39 +02:00
parent f6ab4b5b26
commit 9c3c186da8
6 changed files with 70 additions and 98 deletions

View File

@ -39,10 +39,8 @@ final class CrowdinProvider implements ProviderInterface
private $xliffFileDumper; private $xliffFileDumper;
private $defaultLocale; private $defaultLocale;
private $endpoint; private $endpoint;
private $projectId;
private $filesDownloader;
public function __construct(HttpClientInterface $client, LoaderInterface $loader, LoggerInterface $logger, XliffFileDumper $xliffFileDumper, string $defaultLocale, string $endpoint, int $projectId, HttpClientInterface $filesDownloader) public function __construct(HttpClientInterface $client, LoaderInterface $loader, LoggerInterface $logger, XliffFileDumper $xliffFileDumper, string $defaultLocale, string $endpoint)
{ {
$this->client = $client; $this->client = $client;
$this->loader = $loader; $this->loader = $loader;
@ -50,8 +48,6 @@ final class CrowdinProvider implements ProviderInterface
$this->xliffFileDumper = $xliffFileDumper; $this->xliffFileDumper = $xliffFileDumper;
$this->defaultLocale = $defaultLocale; $this->defaultLocale = $defaultLocale;
$this->endpoint = $endpoint; $this->endpoint = $endpoint;
$this->projectId = $projectId;
$this->filesDownloader = $filesDownloader;
} }
public function __toString(): string public function __toString(): string
@ -102,7 +98,7 @@ final class CrowdinProvider implements ProviderInterface
foreach ($responses as $response) { foreach ($responses as $response) {
if (200 !== $response->getStatusCode()) { if (200 !== $response->getStatusCode()) {
$this->logger->error(sprintf('Unable to upload translations to Crowdin. Message: "%s".', $response->getContent(false))); $this->logger->error(sprintf('Unable to upload translations to Crowdin: "%s".', $response->getContent(false)));
} }
} }
} }
@ -128,39 +124,37 @@ final class CrowdinProvider implements ProviderInterface
$response = $this->downloadSourceFile($fileId); $response = $this->downloadSourceFile($fileId);
} }
$responses[] = [ $responses[] = [$response, $locale, $domain];
'response' => $response,
'locale' => $locale,
'domain' => $domain,
];
} }
} }
foreach ($responses as $responseData) { /* @var ResponseInterface $response */
/** @var ResponseInterface $response */ $downloads = [];
$response = $responseData['response']; foreach ($responses as [$response, $locale, $domain]) {
if (204 === $response->getStatusCode()) { if (204 === $response->getStatusCode()) {
$this->logger->error(sprintf('No content in exported file. Message: "%s".', $response->getContent(false))); $this->logger->error(sprintf('No content in exported file: "%s".', $response->getContent(false)));
continue; continue;
} }
if (200 !== $response->getStatusCode()) { if (200 !== $response->getStatusCode()) {
$this->logger->error(sprintf('Unable to export file. Message: "%s".', $response->getContent(false))); $this->logger->error(sprintf('Unable to export file: "%s".', $response->getContent(false)));
continue; continue;
} }
$exportResponse = $this->filesDownloader->request('GET', $response->toArray()['data']['url']); $response = $this->client->request('GET', $response->toArray()['data']['url']);
$downloads[] = [$response, $locale, $domain];
}
if (200 !== $exportResponse->getStatusCode()) { foreach ($downloads as [$response, $locale, $domain]) {
$this->logger->error(sprintf('Unable to download file content. Message: "%s".', $response->getContent(false))); if (200 !== $response->getStatusCode()) {
$this->logger->error(sprintf('Unable to download file content: "%s".', $response->getContent(false)));
continue; continue;
} }
$translatorBag->addCatalogue($this->loader->load($exportResponse->getContent(), $responseData['locale'], $responseData['domain'])); $translatorBag->addCatalogue($this->loader->load($response->getContent(), $locale, $domain));
} }
return $translatorBag; return $translatorBag;
@ -201,7 +195,7 @@ final class CrowdinProvider implements ProviderInterface
} }
if (204 !== $response->getStatusCode()) { if (204 !== $response->getStatusCode()) {
$this->logger->warning(sprintf('Unable to delete string in project %d. Message: "%s".', $this->projectId, $response->getContent(false))); $this->logger->warning(sprintf('Unable to delete string: "%s".', $response->getContent(false)));
} }
} }
} }
@ -239,7 +233,7 @@ final class CrowdinProvider implements ProviderInterface
* @see https://support.crowdin.com/api/v2/#operation/api.projects.files.getMany (Crowdin API) * @see https://support.crowdin.com/api/v2/#operation/api.projects.files.getMany (Crowdin API)
* @see https://support.crowdin.com/enterprise/api/#operation/api.projects.files.getMany (Crowdin Enterprise API) * @see https://support.crowdin.com/enterprise/api/#operation/api.projects.files.getMany (Crowdin Enterprise API)
*/ */
$response = $this->client->request('POST', sprintf('projects/%s/files', $this->projectId), [ $response = $this->client->request('POST', 'files', [
'json' => [ 'json' => [
'storageId' => $storageId, 'storageId' => $storageId,
'name' => sprintf('%s.%s', $domain, 'xlf'), 'name' => sprintf('%s.%s', $domain, 'xlf'),
@ -247,7 +241,7 @@ final class CrowdinProvider implements ProviderInterface
]); ]);
if (201 !== $response->getStatusCode()) { if (201 !== $response->getStatusCode()) {
$this->logger->error(sprintf('Unable to create a File in Crowdin for domain "%s". Message: "%s".', $domain, $response->getContent(false))); $this->logger->error(sprintf('Unable to create a File in Crowdin for domain "%s": "%s".', $domain, $response->getContent(false)));
return null; return null;
} }
@ -263,14 +257,14 @@ final class CrowdinProvider implements ProviderInterface
* @see https://support.crowdin.com/api/v2/#operation/api.projects.files.put (Crowdin API) * @see https://support.crowdin.com/api/v2/#operation/api.projects.files.put (Crowdin API)
* @see https://support.crowdin.com/enterprise/api/#operation/api.projects.files.put (Crowdin Enterprise API) * @see https://support.crowdin.com/enterprise/api/#operation/api.projects.files.put (Crowdin Enterprise API)
*/ */
$response = $this->client->request('PUT', sprintf('projects/%s/files/%d', $this->projectId, $fileId), [ $response = $this->client->request('PUT', 'files/'.$fileId, [
'json' => [ 'json' => [
'storageId' => $storageId, 'storageId' => $storageId,
], ],
]); ]);
if (200 !== $response->getStatusCode()) { if (200 !== $response->getStatusCode()) {
$this->logger->error(sprintf('Unable to update file in Crowdin for file ID "%d" and domain "%s". Message: "%s".', $fileId, $domain, $response->getContent(false))); $this->logger->error(sprintf('Unable to update file in Crowdin for file ID "%d" and domain "%s": "%s".', $fileId, $domain, $response->getContent(false)));
return null; return null;
} }
@ -286,7 +280,7 @@ final class CrowdinProvider implements ProviderInterface
* @see https://support.crowdin.com/api/v2/#operation/api.projects.translations.postOnLanguage (Crowdin API) * @see https://support.crowdin.com/api/v2/#operation/api.projects.translations.postOnLanguage (Crowdin API)
* @see https://support.crowdin.com/enterprise/api/#operation/api.projects.translations.postOnLanguage (Crowdin Enterprise API) * @see https://support.crowdin.com/enterprise/api/#operation/api.projects.translations.postOnLanguage (Crowdin Enterprise API)
*/ */
return $this->client->request('POST', sprintf('projects/%s/translations/%s', $this->projectId, $locale), [ return $this->client->request('POST', 'translations/'.$locale, [
'json' => [ 'json' => [
'storageId' => $storageId, 'storageId' => $storageId,
'fileId' => $fileId, 'fileId' => $fileId,
@ -300,7 +294,7 @@ final class CrowdinProvider implements ProviderInterface
* @see https://support.crowdin.com/api/v2/#operation/api.projects.translations.exports.post (Crowdin API) * @see https://support.crowdin.com/api/v2/#operation/api.projects.translations.exports.post (Crowdin API)
* @see https://support.crowdin.com/enterprise/api/#operation/api.projects.translations.exports.post (Crowdin Enterprise API) * @see https://support.crowdin.com/enterprise/api/#operation/api.projects.translations.exports.post (Crowdin Enterprise API)
*/ */
return $this->client->request('POST', sprintf('projects/%d/translations/exports', $this->projectId), [ return $this->client->request('POST', 'translations/exports', [
'json' => [ 'json' => [
'targetLanguageId' => $languageId, 'targetLanguageId' => $languageId,
'fileIds' => [$fileId], 'fileIds' => [$fileId],
@ -314,7 +308,7 @@ final class CrowdinProvider implements ProviderInterface
* @see https://support.crowdin.com/api/v2/#operation/api.projects.files.download.get (Crowdin API) * @see https://support.crowdin.com/api/v2/#operation/api.projects.files.download.get (Crowdin API)
* @see https://support.crowdin.com/enterprise/api/#operation/api.projects.files.download.get (Crowdin Enterprise API) * @see https://support.crowdin.com/enterprise/api/#operation/api.projects.files.download.get (Crowdin Enterprise API)
*/ */
return $this->client->request('GET', sprintf('projects/%d/files/%d/download', $this->projectId, $fileId)); return $this->client->request('GET', sprintf('files/%d/download', $fileId));
} }
private function listStrings(int $fileId, int $limit, int $offset): array private function listStrings(int $fileId, int $limit, int $offset): array
@ -323,7 +317,7 @@ final class CrowdinProvider implements ProviderInterface
* @see https://support.crowdin.com/api/v2/#operation/api.projects.strings.getMany (Crowdin API) * @see https://support.crowdin.com/api/v2/#operation/api.projects.strings.getMany (Crowdin API)
* @see https://support.crowdin.com/enterprise/api/#operation/api.projects.strings.getMany (Crowdin Enterprise API) * @see https://support.crowdin.com/enterprise/api/#operation/api.projects.strings.getMany (Crowdin Enterprise API)
*/ */
$response = $this->client->request('GET', sprintf('projects/%d/strings', $this->projectId), [ $response = $this->client->request('GET', 'strings', [
'query' => [ 'query' => [
'fileId' => $fileId, 'fileId' => $fileId,
'limit' => $limit, 'limit' => $limit,
@ -332,7 +326,7 @@ final class CrowdinProvider implements ProviderInterface
]); ]);
if (200 !== $response->getStatusCode()) { if (200 !== $response->getStatusCode()) {
$this->logger->error(sprintf('Unable to list strings for file %d in project %d. Message: "%s".', $fileId, $this->projectId, $response->getContent())); $this->logger->error(sprintf('Unable to list strings for file %d: "%s".', $fileId, $response->getContent()));
return []; return [];
} }
@ -346,7 +340,7 @@ final class CrowdinProvider implements ProviderInterface
* @see https://support.crowdin.com/api/v2/#operation/api.projects.strings.delete (Crowdin API) * @see https://support.crowdin.com/api/v2/#operation/api.projects.strings.delete (Crowdin API)
* @see https://support.crowdin.com/enterprise/api/#operation/api.projects.strings.delete (Crowdin Enterprise API) * @see https://support.crowdin.com/enterprise/api/#operation/api.projects.strings.delete (Crowdin Enterprise API)
*/ */
return $this->client->request('DELETE', sprintf('projects/%d/strings/%d', $this->projectId, $stringId)); return $this->client->request('DELETE', 'strings/'.$stringId);
} }
private function addStorage(string $domain, string $content): int private function addStorage(string $domain, string $content): int
@ -355,7 +349,7 @@ final class CrowdinProvider implements ProviderInterface
* @see https://support.crowdin.com/api/v2/#operation/api.storages.post (Crowdin API) * @see https://support.crowdin.com/api/v2/#operation/api.storages.post (Crowdin API)
* @see https://support.crowdin.com/enterprise/api/#operation/api.storages.post (Crowdin Enterprise API) * @see https://support.crowdin.com/enterprise/api/#operation/api.storages.post (Crowdin Enterprise API)
*/ */
$response = $this->client->request('POST', 'storages', [ $response = $this->client->request('POST', '../../storages', [
'headers' => [ 'headers' => [
'Crowdin-API-FileName' => urlencode(sprintf('%s.%s', $domain, 'xlf')), 'Crowdin-API-FileName' => urlencode(sprintf('%s.%s', $domain, 'xlf')),
'Content-Type' => 'application/octet-stream', 'Content-Type' => 'application/octet-stream',
@ -378,7 +372,7 @@ final class CrowdinProvider implements ProviderInterface
* @see https://support.crowdin.com/api/v2/#operation/api.projects.files.getMany (Crowdin API) * @see https://support.crowdin.com/api/v2/#operation/api.projects.files.getMany (Crowdin API)
* @see https://support.crowdin.com/enterprise/api/#operation/api.projects.files.getMany (Crowdin Enterprise API) * @see https://support.crowdin.com/enterprise/api/#operation/api.projects.files.getMany (Crowdin Enterprise API)
*/ */
$response = $this->client->request('GET', sprintf('projects/%d/files', $this->projectId)); $response = $this->client->request('GET', 'files');
if (200 !== $response->getStatusCode()) { if (200 !== $response->getStatusCode()) {
throw new ProviderException('Unable to list Crowdin files.', $response); throw new ProviderException('Unable to list Crowdin files.', $response);

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\Translation\Bridge\Crowdin; namespace Symfony\Component\Translation\Bridge\Crowdin;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\HttpClient\ScopingHttpClient;
use Symfony\Component\Translation\Dumper\XliffFileDumper; use Symfony\Component\Translation\Dumper\XliffFileDumper;
use Symfony\Component\Translation\Exception\UnsupportedSchemeException; use Symfony\Component\Translation\Exception\UnsupportedSchemeException;
use Symfony\Component\Translation\Loader\LoaderInterface; use Symfony\Component\Translation\Loader\LoaderInterface;
@ -27,8 +28,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface;
*/ */
final class CrowdinProviderFactory extends AbstractProviderFactory final class CrowdinProviderFactory extends AbstractProviderFactory
{ {
private const HOST = 'api.crowdin.com/api/v2/'; private const HOST = 'api.crowdin.com';
private const DSN_OPTION_DOMAIN = 'domain';
/** @var LoaderInterface */ /** @var LoaderInterface */
private $loader; private $loader;
@ -63,34 +63,18 @@ final class CrowdinProviderFactory extends AbstractProviderFactory
throw new UnsupportedSchemeException($dsn, 'crowdin', $this->getSupportedSchemes()); throw new UnsupportedSchemeException($dsn, 'crowdin', $this->getSupportedSchemes());
} }
$host = 'default' === $dsn->getHost() ? $this->getHost($dsn) : $dsn->getHost(); $endpoint = preg_replace('/(^|\.)default$/', '\1'.self::HOST, $dsn->getHost());
$endpoint = sprintf('%s%s', $host, $dsn->getPort() ? ':'.$dsn->getPort() : ''); $endpoint .= $dsn->getPort() ? ':'.$dsn->getPort() : '';
$filesDownloader = $this->client; $client = ScopingHttpClient::forBaseUri($this->client, sprintf('https://%s/api/v2/projects/%d/', $endpoint, $this->getUser($dsn)), [
'auth_bearer' => $this->getPassword($dsn),
], preg_quote('https://'.$endpoint.'/api/v2/'));
$client = $this->client->withOptions([ return new CrowdinProvider($client, $this->loader, $this->logger, $this->xliffFileDumper, $this->defaultLocale, $endpoint);
'base_uri' => 'https://'.$endpoint,
'headers' => [
'Authorization' => 'Bearer '.$this->getPassword($dsn),
],
]);
return new CrowdinProvider($client, $this->loader, $this->logger, $this->xliffFileDumper, $this->defaultLocale, $endpoint, (int) $this->getUser($dsn), $filesDownloader);
} }
protected function getSupportedSchemes(): array protected function getSupportedSchemes(): array
{ {
return ['crowdin']; return ['crowdin'];
} }
protected function getHost(Dsn $dsn): string
{
$organizationDomain = $dsn->getOption(self::DSN_OPTION_DOMAIN);
if ($organizationDomain) {
return sprintf('%s.%s', $organizationDomain, self::HOST);
} else {
return self::HOST;
}
}
} }

View File

@ -17,13 +17,13 @@ class CrowdinProviderFactoryTest extends ProviderFactoryTestCase
public function createProvider(): iterable public function createProvider(): iterable
{ {
yield [ yield [
'crowdin://api.crowdin.com/api/v2/', 'crowdin://api.crowdin.com',
'crowdin://PROJECT_ID:API_TOKEN@default', 'crowdin://PROJECT_ID:API_TOKEN@default',
]; ];
yield [ yield [
'crowdin://ORGANIZATION_DOMAIN.api.crowdin.com/api/v2/', 'crowdin://ORGANIZATION_DOMAIN.api.crowdin.com',
'crowdin://PROJECT_ID:API_TOKEN@default?domain=ORGANIZATION_DOMAIN', 'crowdin://PROJECT_ID:API_TOKEN@ORGANIZATION_DOMAIN.default',
]; ];
} }

View File

@ -18,43 +18,41 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
class CrowdinProviderTest extends ProviderTestCase class CrowdinProviderTest extends ProviderTestCase
{ {
private const PROJECT_ID = 1;
public function createProvider(HttpClientInterface $client, LoaderInterface $loader, LoggerInterface $logger, string $defaultLocale, string $endpoint): ProviderInterface public function createProvider(HttpClientInterface $client, LoaderInterface $loader, LoggerInterface $logger, string $defaultLocale, string $endpoint): ProviderInterface
{ {
return new CrowdinProvider($client, $loader, $logger, $this->getXliffFileDumper(), $defaultLocale, $endpoint, self::PROJECT_ID, new MockHttpClient()); return new CrowdinProvider($client, $loader, $logger, $this->getXliffFileDumper(), $defaultLocale, $endpoint);
} }
public function toStringProvider(): iterable public function toStringProvider(): iterable
{ {
yield [ yield [
$this->createProvider($this->getClient()->withOptions([ $this->createProvider($this->getClient()->withOptions([
'base_uri' => 'https://api.crowdin.com/api/v2/', 'base_uri' => 'https://api.crowdin.com/api/v2/projects/1/',
'headers' => [ 'headers' => [
'Authorization' => 'Bearer API_TOKEN', 'Authorization' => 'Bearer API_TOKEN',
], ],
]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.crowdin.com/api/v2/'), ]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.crowdin.com'),
'crowdin://api.crowdin.com/api/v2/', 'crowdin://api.crowdin.com',
]; ];
yield [ yield [
$this->createProvider($this->getClient()->withOptions([ $this->createProvider($this->getClient()->withOptions([
'base_uri' => 'https://domain.api.crowdin.com/api/v2/', 'base_uri' => 'https://domain.api.crowdin.com/api/v2/projects/1/',
'headers' => [ 'headers' => [
'Authorization' => 'Bearer API_TOKEN', 'Authorization' => 'Bearer API_TOKEN',
], ],
]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'domain.api.crowdin.com/api/v2/'), ]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'domain.api.crowdin.com'),
'crowdin://domain.api.crowdin.com/api/v2/', 'crowdin://domain.api.crowdin.com',
]; ];
yield [ yield [
$this->createProvider($this->getClient()->withOptions([ $this->createProvider($this->getClient()->withOptions([
'base_uri' => 'https://api.crowdin.com/api/v2/:99', 'base_uri' => 'https://api.crowdin.com:99/api/v2/projects/1/',
'headers' => [ 'headers' => [
'Authorization' => 'Bearer API_TOKEN', 'Authorization' => 'Bearer API_TOKEN',
], ],
]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.crowdin.com/api/v2/:99'), ]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.crowdin.com:99'),
'crowdin://api.crowdin.com/api/v2/:99', 'crowdin://api.crowdin.com:99',
]; ];
} }
@ -147,11 +145,11 @@ XLIFF;
])); ]));
$provider = $this->createProvider((new MockHttpClient($responses))->withOptions([ $provider = $this->createProvider((new MockHttpClient($responses))->withOptions([
'base_uri' => 'https://api.crowdin.com/api/v2/', 'base_uri' => 'https://api.crowdin.com/api/v2/projects/1/',
'headers' => [ 'headers' => [
'Authorization' => 'Bearer API_TOKEN', 'Authorization' => 'Bearer API_TOKEN',
], ],
]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.crowdin.com/api/v2/'); ]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.crowdin.com/api/v2/projects/1/');
$provider->write($translatorBag); $provider->write($translatorBag);
} }
@ -220,11 +218,11 @@ XLIFF;
])); ]));
$provider = $this->createProvider((new MockHttpClient($responses))->withOptions([ $provider = $this->createProvider((new MockHttpClient($responses))->withOptions([
'base_uri' => 'https://api.crowdin.com/api/v2/', 'base_uri' => 'https://api.crowdin.com/api/v2/projects/1/',
'headers' => [ 'headers' => [
'Authorization' => 'Bearer API_TOKEN', 'Authorization' => 'Bearer API_TOKEN',
], ],
]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.crowdin.com/api/v2/'); ]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.crowdin.com/api/v2/projects/1/');
$provider->write($translatorBag); $provider->write($translatorBag);
} }
@ -326,11 +324,11 @@ XLIFF;
])); ]));
$provider = $this->createProvider((new MockHttpClient($responses))->withOptions([ $provider = $this->createProvider((new MockHttpClient($responses))->withOptions([
'base_uri' => 'https://api.crowdin.com/api/v2/', 'base_uri' => 'https://api.crowdin.com/api/v2/projects/1/',
'headers' => [ 'headers' => [
'Authorization' => 'Bearer API_TOKEN', 'Authorization' => 'Bearer API_TOKEN',
], ],
]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.crowdin.com/api/v2/'); ]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.crowdin.com/api/v2/projects/1/');
$provider->write($translatorBag); $provider->write($translatorBag);
} }
@ -361,9 +359,6 @@ XLIFF;
return new MockResponse(json_encode(['data' => ['url' => 'https://file.url']])); return new MockResponse(json_encode(['data' => ['url' => 'https://file.url']]));
}, },
];
$filesDownloaderResponses = [
'downloadFile' => function (string $method, string $url) use ($responseContent): ResponseInterface { 'downloadFile' => function (string $method, string $url) use ($responseContent): ResponseInterface {
$this->assertSame('GET', $method); $this->assertSame('GET', $method);
$this->assertSame('https://file.url/', $url); $this->assertSame('https://file.url/', $url);
@ -377,12 +372,12 @@ XLIFF;
->method('load') ->method('load')
->willReturn($expectedTranslatorBag->getCatalogue($locale)); ->willReturn($expectedTranslatorBag->getCatalogue($locale));
$crowdinProvider = new CrowdinProvider((new MockHttpClient($responses))->withOptions([ $crowdinProvider = $this->createProvider((new MockHttpClient($responses))->withOptions([
'base_uri' => 'https://api.crowdin.com/api/v2/', 'base_uri' => 'https://api.crowdin.com/api/v2/projects/1/',
'headers' => [ 'headers' => [
'Authorization' => 'Bearer API_TOKEN', 'Authorization' => 'Bearer API_TOKEN',
], ],
]), $this->getLoader(), $this->getLogger(), $this->getXliffFileDumper(), $this->getDefaultLocale(), 'api.crowdin.com/api/v2', self::PROJECT_ID, new MockHttpClient($filesDownloaderResponses)); ]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.crowdin.com/api/v2');
$translatorBag = $crowdinProvider->read([$domain], [$locale]); $translatorBag = $crowdinProvider->read([$domain], [$locale]);
@ -397,7 +392,7 @@ XLIFF;
$expectedTranslatorBagFr->addCatalogue($arrayLoader->load([ $expectedTranslatorBagFr->addCatalogue($arrayLoader->load([
'index.hello' => 'Bonjour', 'index.hello' => 'Bonjour',
'index.greetings' => 'Bienvenue, {firstname} !', 'index.greetings' => 'Bienvenue, {firstname} !',
], 'fr', 'messages')); ], 'fr'));
yield ['fr', 'messages', <<<'XLIFF' yield ['fr', 'messages', <<<'XLIFF'
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
@ -449,9 +444,6 @@ XLIFF
return new MockResponse(json_encode(['data' => ['url' => 'https://file.url']])); return new MockResponse(json_encode(['data' => ['url' => 'https://file.url']]));
}, },
];
$filesDownloaderResponses = [
'downloadFile' => function (string $method, string $url) use ($responseContent): ResponseInterface { 'downloadFile' => function (string $method, string $url) use ($responseContent): ResponseInterface {
$this->assertSame('GET', $method); $this->assertSame('GET', $method);
$this->assertSame('https://file.url/', $url); $this->assertSame('https://file.url/', $url);
@ -465,12 +457,12 @@ XLIFF
->method('load') ->method('load')
->willReturn($expectedTranslatorBag->getCatalogue($locale)); ->willReturn($expectedTranslatorBag->getCatalogue($locale));
$crowdinProvider = new CrowdinProvider((new MockHttpClient($responses))->withOptions([ $crowdinProvider = $this->createProvider((new MockHttpClient($responses))->withOptions([
'base_uri' => 'https://api.crowdin.com/api/v2/', 'base_uri' => 'https://api.crowdin.com/api/v2/projects/1/',
'headers' => [ 'headers' => [
'Authorization' => 'Bearer API_TOKEN', 'Authorization' => 'Bearer API_TOKEN',
], ],
]), $this->getLoader(), $this->getLogger(), $this->getXliffFileDumper(), $this->getDefaultLocale(), 'api.crowdin.com/api/v2', self::PROJECT_ID, new MockHttpClient($filesDownloaderResponses)); ]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.crowdin.com/api/v2');
$translatorBag = $crowdinProvider->read([$domain], [$locale]); $translatorBag = $crowdinProvider->read([$domain], [$locale]);
@ -574,11 +566,11 @@ XLIFF
])); ]));
$provider = $this->createProvider((new MockHttpClient($responses))->withOptions([ $provider = $this->createProvider((new MockHttpClient($responses))->withOptions([
'base_uri' => 'https://api.crowdin.com/api/v2/', 'base_uri' => 'https://api.crowdin.com/api/v2/projects/1/',
'headers' => [ 'headers' => [
'Authorization' => 'Bearer API_TOKEN', 'Authorization' => 'Bearer API_TOKEN',
], ],
]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.crowdin.com/api/v2/'); ]), $this->getLoader(), $this->getLogger(), $this->getDefaultLocale(), 'api.crowdin.com/api/v2/projects/1/');
$provider->delete($translatorBag); $provider->delete($translatorBag);
} }

View File

@ -26,7 +26,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface;
*/ */
final class LocoProviderFactory extends AbstractProviderFactory final class LocoProviderFactory extends AbstractProviderFactory
{ {
private const HOST = 'localise.biz/api/'; private const HOST = 'localise.biz';
private $client; private $client;
private $logger; private $logger;
@ -50,9 +50,11 @@ final class LocoProviderFactory extends AbstractProviderFactory
throw new UnsupportedSchemeException($dsn, 'loco', $this->getSupportedSchemes()); throw new UnsupportedSchemeException($dsn, 'loco', $this->getSupportedSchemes());
} }
$endpoint = sprintf('%s%s', 'default' === $dsn->getHost() ? self::HOST : $dsn->getHost(), $dsn->getPort() ? ':'.$dsn->getPort() : ''); $endpoint = 'default' === $dsn->getHost() ? self::HOST : $dsn->getHost();
$endpoint .= $dsn->getPort() ? ':'.$dsn->getPort() : '';
$client = $this->client->withOptions([ $client = $this->client->withOptions([
'base_uri' => 'https://'.$endpoint, 'base_uri' => 'https://'.$endpoint.'/api/',
'headers' => [ 'headers' => [
'Authorization' => 'Loco '.$this->getUser($dsn), 'Authorization' => 'Loco '.$this->getUser($dsn),
], ],

View File

@ -22,7 +22,7 @@ class LocoProviderFactoryTest extends ProviderFactoryTestCase
public function createProvider(): iterable public function createProvider(): iterable
{ {
yield [ yield [
'loco://localise.biz/api/', 'loco://localise.biz',
'loco://API_KEY@default', 'loco://API_KEY@default',
]; ];
} }