From e5cca62403a089dbecec399a16cd6c4021a15d1c Mon Sep 17 00:00:00 2001 From: Max Inno Date: Fri, 11 Sep 2026 21:26:58 +0300 Subject: [PATCH] feat: add support for organization-level Concordance search --- src/CrowdinApiClient/Api/GlossaryApi.php | 25 +++++++++++ .../Api/TranslationMemoryApi.php | 27 ++++++++++++ .../CrowdinApiClient/Api/GlossaryApiTest.php | 35 +++++++++++++++ .../Api/TranslationMemoryApiTest.php | 43 +++++++++++++++++++ 4 files changed, 130 insertions(+) diff --git a/src/CrowdinApiClient/Api/GlossaryApi.php b/src/CrowdinApiClient/Api/GlossaryApi.php index ddb50862..d273a13c 100644 --- a/src/CrowdinApiClient/Api/GlossaryApi.php +++ b/src/CrowdinApiClient/Api/GlossaryApi.php @@ -210,6 +210,31 @@ public function concordance(int $projectId, array $data): ?ModelCollection ); } + /** + * Concordance search in organization Glossaries + * @link https://developer.crowdin.com/api/v2/#operation/api.glossaries.concordance.post API Documentation + * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.glossaries.concordance.post API Documentation Enterprise + * + * @param array $params + * string $params[sourceLanguageId] required
+ * string $params[targetLanguageId] required
+ * string[] $params[expressions] required
+ * integer $params[userId] Crowdin API only
+ * @return ModelCollection|null + */ + public function searchOrganizationConcordance(array $params): ?ModelCollection + { + return $this->client->apiRequest( + 'post', + 'glossaries/concordance', + new ResponseModelListDecorator(GlossaryConcordance::class), + [ + 'body' => json_encode($params), + 'headers' => $this->getHeaders(), + ] + ); + } + /** * List Terms * @link https://developer.crowdin.com/api/v2/#operation/api.glossaries.terms.getMany API Documentation diff --git a/src/CrowdinApiClient/Api/TranslationMemoryApi.php b/src/CrowdinApiClient/Api/TranslationMemoryApi.php index b746be16..82d9419c 100644 --- a/src/CrowdinApiClient/Api/TranslationMemoryApi.php +++ b/src/CrowdinApiClient/Api/TranslationMemoryApi.php @@ -289,4 +289,31 @@ public function concordance(int $projectId, array $params): ?ModelCollection ] ); } + + /** + * Concordance search in organization TMs + * @link https://developer.crowdin.com/api/v2/#operation/api.tms.concordance.post API Documentation + * @link https://developer.crowdin.com/enterprise/api/v2/#operation/api.tms.concordance.post API Documentation Enterprise + * + * @param array $params + * string $params[sourceLanguageId] required
+ * string $params[targetLanguageId] required
+ * bool $params[autoSubstitution] required Improves TM suggestions
+ * int $params[minRelevant] required Show TM suggestions with specified minimum match (1-100)
+ * string[] $params[expressions] required
+ * integer $params[userId] Crowdin API only
+ * @return ModelCollection|null + */ + public function searchOrganizationConcordance(array $params): ?ModelCollection + { + return $this->client->apiRequest( + 'post', + 'tms/concordance', + new ResponseModelListDecorator(TranslationMemoryConcordance::class), + [ + 'body' => json_encode($params), + 'headers' => $this->getHeaders(), + ] + ); + } } diff --git a/tests/CrowdinApiClient/Api/GlossaryApiTest.php b/tests/CrowdinApiClient/Api/GlossaryApiTest.php index 68eba790..3846827c 100644 --- a/tests/CrowdinApiClient/Api/GlossaryApiTest.php +++ b/tests/CrowdinApiClient/Api/GlossaryApiTest.php @@ -389,6 +389,41 @@ public function testConcordance(): void $this->assertEquals(7, $concordance[0]->getTargetTerms()[0]->getId()); } + public function testSearchOrganizationConcordance(): void + { + $data = [ + 'sourceLanguageId' => 'en', + 'targetLanguageId' => 'fr', + 'expressions' => ['Welcome!'], + 'userId' => 6, + ]; + + $this->mockRequest([ + 'path' => '/glossaries/concordance', + 'method' => 'post', + 'body' => json_encode($data), + 'response' => json_encode([ + 'data' => [ + [ + 'data' => [ + 'glossary' => ['id' => 2], + 'concept' => ['id' => 8], + 'sourceTerms' => [], + 'targetTerms' => [], + ], + ], + ], + ]), + ]); + + $concordance = $this->crowdin->glossary->searchOrganizationConcordance($data); + + $this->assertInstanceOf(ModelCollection::class, $concordance); + $this->assertCount(1, $concordance); + $this->assertEquals(2, $concordance[0]->getGlossary()['id']); + $this->assertEquals(8, $concordance[0]->getConcept()['id']); + } + public function testListTerms(): void { $this->mockRequestGet( diff --git a/tests/CrowdinApiClient/Api/TranslationMemoryApiTest.php b/tests/CrowdinApiClient/Api/TranslationMemoryApiTest.php index 49fdae5c..d518f484 100644 --- a/tests/CrowdinApiClient/Api/TranslationMemoryApiTest.php +++ b/tests/CrowdinApiClient/Api/TranslationMemoryApiTest.php @@ -354,6 +354,49 @@ public function testConcordance(): void $this->assertEquals(4, $concordance[0]->getTm()->getId()); } + public function testSearchOrganizationConcordance(): void + { + $params = [ + 'sourceLanguageId' => 'en', + 'targetLanguageId' => 'de', + 'autoSubstitution' => true, + 'minRelevant' => 60, + 'expressions' => ['Welcome!'], + 'userId' => 6, + ]; + + $this->mockRequest([ + 'path' => '/tms/concordance', + 'method' => 'post', + 'body' => json_encode($params), + 'response' => json_encode([ + 'data' => [ + [ + 'data' => [ + 'tm' => [ + 'id' => 4, + 'name' => 'Knowledge Base TM', + ], + 'recordId' => 34, + 'source' => 'Welcome!', + 'target' => 'Ласкаво просимо!', + 'relevant' => 100, + 'substituted' => null, + 'updatedAt' => null, + ], + ], + ], + ]), + ]); + + $concordance = $this->crowdin->translationMemory->searchOrganizationConcordance($params); + + $this->assertInstanceOf(ModelCollection::class, $concordance); + $this->assertCount(1, $concordance); + $this->assertInstanceOf(TranslationMemoryConcordance::class, $concordance[0]); + $this->assertEquals(4, $concordance[0]->getTm()->getId()); + } + public function testListSegments() { $this->mockRequest([