Skip to content

Commit a1d2f67

Browse files
andrii-bodnarnicolas-grekas
authored andcommitted
[Translation] Crowdin Bridge: use the project language mapping
1 parent 3cbbe26 commit a1d2f67

File tree

2 files changed

+123
-87
lines changed

2 files changed

+123
-87
lines changed

src/Symfony/Component/Translation/Bridge/Crowdin/CrowdinProvider.php

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,13 @@ public function __toString(): string
5656
public function write(TranslatorBagInterface $translatorBag): void
5757
{
5858
$fileList = $this->getFileList();
59+
$languageMapping = $this->getLanguageMapping();
5960

6061
$responses = [];
6162

6263
foreach ($translatorBag->getCatalogues() as $catalogue) {
64+
$locale = $catalogue->getLocale();
65+
6366
foreach ($catalogue->getDomains() as $domain) {
6467
if (0 === \count($catalogue->all($domain))) {
6568
continue;
@@ -86,7 +89,7 @@ public function write(TranslatorBagInterface $translatorBag): void
8689
continue;
8790
}
8891

89-
$responses[] = $this->uploadTranslations($fileId, $domain, $content, $catalogue->getLocale());
92+
$responses[] = $this->uploadTranslations($fileId, $domain, $content, $languageMapping[$locale] ?? $locale);
9093
}
9194
}
9295
}
@@ -105,12 +108,11 @@ public function write(TranslatorBagInterface $translatorBag): void
105108
public function read(array $domains, array $locales): TranslatorBag
106109
{
107110
$fileList = $this->getFileList();
111+
$languageMapping = $this->getLanguageMapping();
108112

109113
$translatorBag = new TranslatorBag();
110114
$responses = [];
111115

112-
$localeLanguageMap = $this->mapLocalesToLanguageId($locales);
113-
114116
foreach ($domains as $domain) {
115117
$fileId = $this->getFileIdByDomain($fileList, $domain);
116118

@@ -120,7 +122,7 @@ public function read(array $domains, array $locales): TranslatorBag
120122

121123
foreach ($locales as $locale) {
122124
if ($locale !== $this->defaultLocale) {
123-
$response = $this->exportProjectTranslations($localeLanguageMap[$locale], $fileId);
125+
$response = $this->exportProjectTranslations($languageMapping[$locale] ?? $locale, $fileId);
124126
} else {
125127
$response = $this->downloadSourceFile($fileId);
126128
}
@@ -406,37 +408,24 @@ private function getFileList(): array
406408
return $result;
407409
}
408410

409-
private function mapLocalesToLanguageId(array $locales): array
411+
private function getLanguageMapping(): array
410412
{
411413
/**
412-
* We cannot query by locales, we need to fetch all and filter out the relevant ones.
413-
*
414-
* @see https://developer.crowdin.com/api/v2/#operation/api.languages.getMany (Crowdin API)
415-
* @see https://developer.crowdin.com/enterprise/api/v2/#operation/api.languages.getMany (Crowdin Enterprise API)
414+
* @see https://developer.crowdin.com/api/v2/#operation/api.projects.get (Crowdin API)
415+
* @see https://developer.crowdin.com/enterprise/api/v2/#operation/api.projects.get (Crowdin Enterprise API)
416416
*/
417-
$response = $this->client->request('GET', '../../languages?limit=500');
417+
$response = $this->client->request('GET', '');
418418

419419
if (200 !== $response->getStatusCode()) {
420-
throw new ProviderException('Unable to list set languages.', $response);
420+
throw new ProviderException('Unable to get project info.', $response);
421421
}
422422

423-
$localeLanguageMap = [];
424-
foreach ($response->toArray()['data'] as $language) {
425-
foreach (['locale', 'osxLocale', 'id'] as $key) {
426-
if (\in_array($language['data'][$key], $locales, true)) {
427-
$localeLanguageMap[$language['data'][$key]] = $language['data']['id'];
428-
}
429-
}
430-
}
431-
432-
if (\count($localeLanguageMap) !== \count($locales)) {
433-
$message = implode('", "', array_diff($locales, array_keys($localeLanguageMap)));
434-
$message = sprintf('Unable to find all requested locales: "%s" not found.', $message);
435-
$this->logger->error($message);
436-
437-
throw new ProviderException($message, $response);
423+
$projectInfo = $response->toArray()['data'];
424+
$mapping = [];
425+
foreach ($projectInfo['languageMapping'] ?? [] as $key => $value) {
426+
$mapping[$value['locale']] = $key;
438427
}
439428

440-
return $localeLanguageMap;
429+
return $mapping;
441430
}
442431
}

src/Symfony/Component/Translation/Bridge/Crowdin/Tests/CrowdinProviderTest.php

Lines changed: 107 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ public function testCompleteWriteProcessAddFiles()
110110

111111
return new MockResponse(json_encode(['data' => []]));
112112
},
113+
'getProject' => function (string $method, string $url): ResponseInterface {
114+
$this->assertSame('GET', $method);
115+
$this->assertSame('https://api.crowdin.com/api/v2/projects/1/', $url);
116+
117+
return new MockResponse(json_encode(['data' => ['languageMapping' => []]]));
118+
},
113119
'addStorage' => function (string $method, string $url, array $options = []) use ($expectedMessagesFileContent): ResponseInterface {
114120
$this->assertSame('POST', $method);
115121
$this->assertSame('https://api.crowdin.com/api/v2/storages', $url);
@@ -188,6 +194,12 @@ public function testWriteAddFileServerError()
188194

189195
return new MockResponse(json_encode(['data' => []]));
190196
},
197+
'getProject' => function (string $method, string $url): ResponseInterface {
198+
$this->assertSame('GET', $method);
199+
$this->assertSame('https://api.crowdin.com/api/v2/projects/1/', $url);
200+
201+
return new MockResponse(json_encode(['data' => ['languageMapping' => []]]));
202+
},
191203
'addStorage' => function (string $method, string $url, array $options = []) use ($expectedMessagesFileContent): ResponseInterface {
192204
$this->assertSame('POST', $method);
193205
$this->assertSame('https://api.crowdin.com/api/v2/storages', $url);
@@ -260,6 +272,12 @@ public function testWriteUpdateFileServerError()
260272
],
261273
]));
262274
},
275+
'getProject' => function (string $method, string $url): ResponseInterface {
276+
$this->assertSame('GET', $method);
277+
$this->assertSame('https://api.crowdin.com/api/v2/projects/1/', $url);
278+
279+
return new MockResponse(json_encode(['data' => ['languageMapping' => []]]));
280+
},
263281
'addStorage' => function (string $method, string $url, array $options = []) use ($expectedMessagesFileContent): ResponseInterface {
264282
$this->assertSame('POST', $method);
265283
$this->assertSame('https://api.crowdin.com/api/v2/storages', $url);
@@ -349,6 +367,12 @@ public function testWriteUploadTranslationsServerError()
349367
],
350368
]));
351369
},
370+
'getProject' => function (string $method, string $url): ResponseInterface {
371+
$this->assertSame('GET', $method);
372+
$this->assertSame('https://api.crowdin.com/api/v2/projects/1/', $url);
373+
374+
return new MockResponse(json_encode(['data' => ['languageMapping' => []]]));
375+
},
352376
'addStorage' => function (string $method, string $url, array $options = []) use ($expectedMessagesFileContent): ResponseInterface {
353377
$this->assertSame('POST', $method);
354378
$this->assertSame('https://api.crowdin.com/api/v2/storages', $url);
@@ -442,6 +466,12 @@ public function testCompleteWriteProcessUpdateFiles()
442466
],
443467
]));
444468
},
469+
'getProject' => function (string $method, string $url): ResponseInterface {
470+
$this->assertSame('GET', $method);
471+
$this->assertSame('https://api.crowdin.com/api/v2/projects/1/', $url);
472+
473+
return new MockResponse(json_encode(['data' => ['languageMapping' => []]]));
474+
},
445475
'addStorage' => function (string $method, string $url, array $options = []) use ($expectedMessagesFileContent): ResponseInterface {
446476
$this->assertSame('POST', $method);
447477
$this->assertSame('https://api.crowdin.com/api/v2/storages', $url);
@@ -512,6 +542,20 @@ public function testCompleteWriteProcessAddFileAndUploadTranslations(TranslatorB
512542
],
513543
]));
514544
},
545+
'getProject' => function (string $method, string $url): ResponseInterface {
546+
$this->assertSame('GET', $method);
547+
$this->assertSame('https://api.crowdin.com/api/v2/projects/1/', $url);
548+
549+
return new MockResponse(json_encode([
550+
'data' => [
551+
'languageMapping' => [
552+
'pt-PT' => [
553+
'locale' => 'pt',
554+
],
555+
],
556+
],
557+
]));
558+
},
515559
'addStorage' => function (string $method, string $url, array $options = []) use ($expectedMessagesFileContent): ResponseInterface {
516560
$this->assertSame('POST', $method);
517561
$this->assertSame('https://api.crowdin.com/api/v2/storages', $url);
@@ -542,6 +586,22 @@ public function testCompleteWriteProcessAddFileAndUploadTranslations(TranslatorB
542586
$this->assertSame(sprintf('https://api.crowdin.com/api/v2/projects/1/translations/%s', $expectedLocale), $url);
543587
$this->assertSame('{"storageId":19,"fileId":12}', $options['body']);
544588

589+
return new MockResponse();
590+
},
591+
'addStorage3' => function (string $method, string $url, array $options = []) use ($expectedMessagesTranslationsContent): ResponseInterface {
592+
$this->assertSame('POST', $method);
593+
$this->assertSame('https://api.crowdin.com/api/v2/storages', $url);
594+
$this->assertSame('Content-Type: application/octet-stream', $options['normalized_headers']['content-type'][0]);
595+
$this->assertSame('Crowdin-API-FileName: messages.xlf', $options['normalized_headers']['crowdin-api-filename'][0]);
596+
$this->assertStringMatchesFormat($expectedMessagesTranslationsContent, $options['body']);
597+
598+
return new MockResponse(json_encode(['data' => ['id' => 19]], ['http_code' => 201]));
599+
},
600+
'uploadTranslations2' => function (string $method, string $url, array $options = []) use ($expectedLocale): ResponseInterface {
601+
$this->assertSame('POST', $method);
602+
$this->assertSame(sprintf('https://api.crowdin.com/api/v2/projects/1/translations/%s', $expectedLocale), $url);
603+
$this->assertSame('{"storageId":19,"fileId":12}', $options['body']);
604+
545605
return new MockResponse();
546606
},
547607
];
@@ -582,6 +642,33 @@ public static function getResponsesForProcessAddFileAndUploadTranslations(): \Ge
582642
</file>
583643
</xliff>
584644

645+
XLIFF
646+
];
647+
648+
$translatorBagPt = new TranslatorBag();
649+
$translatorBagPt->addCatalogue($arrayLoader->load([
650+
'a' => 'trans_en_a',
651+
], 'en'));
652+
$translatorBagPt->addCatalogue($arrayLoader->load([
653+
'a' => 'trans_pt_a',
654+
], 'pt'));
655+
656+
yield [$translatorBagPt, 'pt-PT', <<<'XLIFF'
657+
<?xml version="1.0" encoding="utf-8"?>
658+
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
659+
<file source-language="en" target-language="pt" datatype="plaintext" original="file.ext">
660+
<header>
661+
<tool tool-id="symfony" tool-name="Symfony"/>
662+
</header>
663+
<body>
664+
<trans-unit id="ypeBEso" resname="a">
665+
<source>a</source>
666+
<target>trans_pt_a</target>
667+
</trans-unit>
668+
</body>
669+
</file>
670+
</xliff>
671+
585672
XLIFF
586673
];
587674

@@ -632,25 +719,15 @@ public function testReadForOneLocaleAndOneDomain(string $locale, string $domain,
632719
],
633720
]));
634721
},
635-
'listLanguages' => function (string $method, string $url, array $options = []): ResponseInterface {
722+
'getProject' => function (string $method, string $url): ResponseInterface {
636723
$this->assertSame('GET', $method);
637-
$this->assertSame('https://api.crowdin.com/api/v2/languages?limit=500', $url);
638-
$this->assertSame('Authorization: Bearer API_TOKEN', $options['normalized_headers']['authorization'][0]);
724+
$this->assertSame('https://api.crowdin.com/api/v2/projects/1/', $url);
639725

640726
return new MockResponse(json_encode([
641727
'data' => [
642-
[
643-
'data' => [
644-
'id' => 'en-GB',
645-
'osxLocale' => 'en_GB',
646-
'locale' => 'en-GB',
647-
],
648-
],
649-
[
650-
'data' => [
651-
'id' => 'fr',
652-
'osxLocale' => 'fr_FR',
653-
'locale' => 'fr-FR',
728+
'languageMapping' => [
729+
'pt-PT' => [
730+
'locale' => 'pt',
654731
],
655732
],
656733
],
@@ -770,25 +847,15 @@ public function testReadForDefaultLocaleAndOneDomain(string $locale, string $dom
770847
],
771848
]));
772849
},
773-
'listLanguages' => function (string $method, string $url, array $options = []): ResponseInterface {
850+
'getProject' => function (string $method, string $url): ResponseInterface {
774851
$this->assertSame('GET', $method);
775-
$this->assertSame('https://api.crowdin.com/api/v2/languages?limit=500', $url);
776-
$this->assertSame('Authorization: Bearer API_TOKEN', $options['normalized_headers']['authorization'][0]);
852+
$this->assertSame('https://api.crowdin.com/api/v2/projects/1/', $url);
777853

778854
return new MockResponse(json_encode([
779855
'data' => [
780-
[
781-
'data' => [
782-
'id' => 'en',
783-
'osxLocale' => 'en_GB',
784-
'locale' => 'en-GB',
785-
],
786-
],
787-
[
788-
'data' => [
789-
'id' => 'fr',
790-
'osxLocale' => 'fr_FR',
791-
'locale' => 'fr-FR',
856+
'languageMapping' => [
857+
'pt-PT' => [
858+
'locale' => 'pt',
792859
],
793860
],
794861
],
@@ -874,25 +941,15 @@ public function testReadServerException()
874941
],
875942
]));
876943
},
877-
'listLanguages' => function (string $method, string $url, array $options = []): ResponseInterface {
944+
'getProject' => function (string $method, string $url): ResponseInterface {
878945
$this->assertSame('GET', $method);
879-
$this->assertSame('https://api.crowdin.com/api/v2/languages?limit=500', $url);
880-
$this->assertSame('Authorization: Bearer API_TOKEN', $options['normalized_headers']['authorization'][0]);
946+
$this->assertSame('https://api.crowdin.com/api/v2/projects/1/', $url);
881947

882948
return new MockResponse(json_encode([
883949
'data' => [
884-
[
885-
'data' => [
886-
'id' => 'en',
887-
'osxLocale' => 'en_GB',
888-
'locale' => 'en-GB',
889-
],
890-
],
891-
[
892-
'data' => [
893-
'id' => 'fr',
894-
'osxLocale' => 'fr_FR',
895-
'locale' => 'fr-FR',
950+
'languageMapping' => [
951+
'pt-PT' => [
952+
'locale' => 'pt',
896953
],
897954
],
898955
],
@@ -933,25 +990,15 @@ public function testReadDownloadServerException()
933990
],
934991
]));
935992
},
936-
'listLanguages' => function (string $method, string $url, array $options = []): ResponseInterface {
993+
'getProject' => function (string $method, string $url): ResponseInterface {
937994
$this->assertSame('GET', $method);
938-
$this->assertSame('https://api.crowdin.com/api/v2/languages?limit=500', $url);
939-
$this->assertSame('Authorization: Bearer API_TOKEN', $options['normalized_headers']['authorization'][0]);
995+
$this->assertSame('https://api.crowdin.com/api/v2/projects/1/', $url);
940996

941997
return new MockResponse(json_encode([
942998
'data' => [
943-
[
944-
'data' => [
945-
'id' => 'en',
946-
'osxLocale' => 'en_GB',
947-
'locale' => 'en-GB',
948-
],
949-
],
950-
[
951-
'data' => [
952-
'id' => 'fr',
953-
'osxLocale' => 'fr_FR',
954-
'locale' => 'fr-FR',
999+
'languageMapping' => [
1000+
'pt-PT' => [
1001+
'locale' => 'pt',
9551002
],
9561003
],
9571004
],

0 commit comments

Comments
 (0)