Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
16 changes: 16 additions & 0 deletions src/Crowdin.Api/AI/AiApiExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,22 @@ public async Task<ResponseList<AiSupportedProviderModel>> ListSupportedAiProvide
return _jsonParser.ParseResponseList<AiSupportedProviderModel>(result.JsonObject);
}

/// <summary>
/// AI Translate Strings. Documentation:
/// <a href="https://support.crowdin.com/developer/api/v2/#tag/AI/operation/api.users.ai.translate.strings.post">Crowdin File Based API</a>
/// <a href="https://support.crowdin.com/developer/api/v2/string-based/#tag/AI/operation/api.users.ai.translate.strings.post">Crowdin String Based API</a>
/// <a href="https://support.crowdin.com/developer/enterprise/api/v2/#tag/AI/operation/api.ai.translate.strings.post">Crowdin Enterprise API</a>
/// </summary>
[PublicAPI]
public async Task<AiTranslateStringsResponse> TranslateStrings(
long? userId,
AiTranslateStringsRequest request)
{
string url = AddUserIdIfAvailable(userId, "/ai/translate");
CrowdinApiResult result = await _apiClient.SendPostRequest(url, request);
return _jsonParser.ParseResponseObject<AiTranslateStringsResponse>(result.JsonObject);
}

#region Helper methods

private static string AddUserIdIfAvailable(long? userId, string baseUrl)
Expand Down
40 changes: 40 additions & 0 deletions src/Crowdin.Api/AI/AiTranslateStringsRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using Newtonsoft.Json;

namespace Crowdin.Api.AI
{
[PublicAPI]
public class AiTranslateStringsRequest
{
[JsonProperty("strings")]
public ICollection<string> Strings { get; set; }

[JsonProperty("targetLanguageId")]
public string TargetLanguageId { get; set; }

[JsonProperty("sourceLanguageId")]
public string? SourceLanguageId { get; set; }

[JsonProperty("tmIds")]
public ICollection<long>? TmIds { get; set; }

[JsonProperty("glossaryIds")]
public ICollection<long>? GlossaryIds { get; set; }

[JsonProperty("aiPromptId")]
public long? AiPromptId { get; set; }

[JsonProperty("aiProviderId")]
public long? AiProviderId { get; set; }

[JsonProperty("aiModelId")]
public string? AiModelId { get; set; }

[JsonProperty("instructions")]
public ICollection<string>? Instructions { get; set; }

[JsonProperty("attachmentIds")]
public ICollection<long>? AttachmentIds { get; set; }
}
}
19 changes: 19 additions & 0 deletions src/Crowdin.Api/AI/AiTranslateStringsResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using Newtonsoft.Json;

namespace Crowdin.Api.AI
{
[PublicAPI]
public class AiTranslateStringsResponse
{
[JsonProperty("sourceLanguageId")]
public string SourceLanguageId { get; set; } = null!;

[JsonProperty("targetLanguageId")]
public string TargetLanguageId { get; set; } = null!;

[JsonProperty("translations")]
public ICollection<string> Translations { get; set; } = null!;
}
}
3 changes: 3 additions & 0 deletions src/Crowdin.Api/AI/IAiApiExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,5 +120,8 @@ Task<ResponseList<AiSupportedProviderModel>> ListSupportedAiProviderModels(
string? providerType = null,
bool? enabled = null,
string? orderBy = null);
Task<AiTranslateStringsResponse> TranslateStrings(
long? userId,
AiTranslateStringsRequest request);
}
}
9 changes: 9 additions & 0 deletions tests/Crowdin.Api.UnitTesting/Crowdin.Api.UnitTesting.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Tasks_Comments.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Resources\AI_TranslateStrings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>AI_TranslateStrings.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>

<ItemGroup>
Expand Down Expand Up @@ -563,6 +567,11 @@
<AutoGen>True</AutoGen>
<DependentUpon>Tasks_Comments.resx</DependentUpon>
</Compile>
<Compile Update="Resources\AI_TranslateStrings.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>AI_TranslateStrings.resx</DependentUpon>
</Compile>
</ItemGroup>

<PropertyGroup>
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 55 additions & 0 deletions tests/Crowdin.Api.UnitTesting/Resources/AI_TranslateStrings.resx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>

<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">

</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="TranslateStrings_Request" xml:space="preserve">
<value>{
"strings": [
"Some text to translate!"
],
"targetLanguageId": "uk",
"sourceLanguageId": "en",
"tmIds": [
123
],
"glossaryIds": [
456
],
"aiPromptId": 789,
"instructions": [
"Keep a formal tone"
],
"attachmentIds": [
123
]
}</value>
</data>
<data name="CommonResponses_TranslateStrings" xml:space="preserve">
<value>{
"data": {
"sourceLanguageId": "en",
"targetLanguageId": "uk",
"translations": [
"Перекладений текст 1",
"Перекладений текст 2"
]
}
}</value>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using System;
using System.Net;
using System.Threading.Tasks;
using Crowdin.Api.AI;
using Crowdin.Api.Core;
using Crowdin.Api.UnitTesting.Resources;
using Moq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Xunit;

namespace Crowdin.Api.UnitTesting.Tests.AI;

public class AiTranslateStringsApiTests
{
private static readonly JsonSerializerSettings JsonSettings = TestUtils.CreateJsonSerializerOptions();

[Fact]
public async Task TranslateStrings()
{
const int userId = 1;

var request = new AiTranslateStringsRequest
{
Strings = new[] { "Some text to translate!" },
TargetLanguageId = "uk",
SourceLanguageId = "en",
TmIds = new[] { 123L },
GlossaryIds = new[] { 456L },
AiPromptId = 789,
Instructions = new[] { "Keep a formal tone" },
AttachmentIds = new[] { 123L }
};

string actualRequestJson = JsonConvert.SerializeObject(request, JsonSettings);
string expectedRequestJson = TestUtils.CompactJson(AI_TranslateStrings.TranslateStrings_Request);
Assert.Equal(expectedRequestJson, actualRequestJson);

Mock<ICrowdinApiClient> mockClient = TestUtils.CreateMockClientWithDefaultParser();

var url = $"/users/{userId}/ai/translate";

mockClient
.Setup(client => client.SendPostRequest(url, request, null))
.ReturnsAsync(new CrowdinApiResult
{
StatusCode = HttpStatusCode.OK,
JsonObject = JObject.Parse(AI_TranslateStrings.CommonResponses_TranslateStrings)
});

var executor = new AiApiExecutor(mockClient.Object);
AiTranslateStringsResponse response = await executor.TranslateStrings(userId, request);

Assert_TranslateStrings(response);
}

private static void Assert_TranslateStrings(AiTranslateStringsResponse? response)
{
ArgumentNullException.ThrowIfNull(response);

Assert.Equal("en", response.SourceLanguageId);
Assert.Equal("uk", response.TargetLanguageId);
Assert.NotNull(response.Translations);
Assert.Equal(2, response.Translations.Count);
}
}
Loading