diff --git a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj
index 1da4bfacc..2fff98d70 100644
--- a/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj
+++ b/backend/src/Squidex.Infrastructure/Squidex.Infrastructure.csproj
@@ -30,7 +30,7 @@
-
+
diff --git a/backend/src/Squidex.Infrastructure/Translations/DeepLTranslator.cs b/backend/src/Squidex.Infrastructure/Translations/DeepLTranslator.cs
deleted file mode 100644
index 29023355c..000000000
--- a/backend/src/Squidex.Infrastructure/Translations/DeepLTranslator.cs
+++ /dev/null
@@ -1,101 +0,0 @@
-// ==========================================================================
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex UG (haftungsbeschraenkt)
-// All rights reserved. Licensed under the MIT license.
-// ==========================================================================
-
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Net;
-using System.Net.Http;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Options;
-using Squidex.Infrastructure.Json;
-
-namespace Squidex.Infrastructure.Translations
-{
- [ExcludeFromCodeCoverage]
- public sealed class DeepLTranslator : ITranslator
- {
- private const string Url = "https://api.deepl.com/v2/translate";
- private readonly HttpClient httpClient = new HttpClient();
- private readonly DeepLTranslatorOptions deeplOptions;
- private readonly IJsonSerializer jsonSerializer;
-
- private sealed class Response
- {
- public ResponseTranslation[] Translations { get; set; }
- }
-
- private sealed class ResponseTranslation
- {
- public string Text { get; set; }
- }
-
- public DeepLTranslator(IOptions deeplOptions, IJsonSerializer jsonSerializer)
- {
- Guard.NotNull(deeplOptions, nameof(deeplOptions));
- Guard.NotNull(jsonSerializer, nameof(jsonSerializer));
-
- this.deeplOptions = deeplOptions.Value;
-
- this.jsonSerializer = jsonSerializer;
- }
-
- public async Task Translate(string sourceText, Language targetLanguage, Language? sourceLanguage = null, CancellationToken ct = default)
- {
- if (string.IsNullOrWhiteSpace(sourceText) || targetLanguage == null)
- {
- return new Translation(TranslationResult.NotTranslated, sourceText);
- }
-
- if (string.IsNullOrWhiteSpace(deeplOptions.AuthKey))
- {
- return new Translation(TranslationResult.NotImplemented);
- }
-
- var parameters = new Dictionary
- {
- ["auth_key"] = deeplOptions.AuthKey,
- ["text"] = sourceText,
- ["target_lang"] = GetLanguageCode(targetLanguage)
- };
-
- if (sourceLanguage != null)
- {
- parameters["source_lang"] = GetLanguageCode(sourceLanguage);
- }
-
- var body = new FormUrlEncodedContent(parameters!);
-
- using (var response = await httpClient.PostAsync(Url, body, ct))
- {
- var responseString = await response.Content.ReadAsStringAsync(ct);
-
- if (response.IsSuccessStatusCode)
- {
- var result = jsonSerializer.Deserialize(responseString);
-
- if (result?.Translations?.Length == 1)
- {
- return new Translation(TranslationResult.Translated, result.Translations[0].Text);
- }
- }
-
- if (response.StatusCode == HttpStatusCode.BadRequest)
- {
- return new Translation(TranslationResult.LanguageNotSupported, resultText: responseString);
- }
-
- return new Translation(TranslationResult.Failed, resultText: responseString);
- }
- }
-
- private static string GetLanguageCode(Language language)
- {
- return language.Iso2Code.Substring(0, 2).ToUpperInvariant();
- }
- }
-}
diff --git a/backend/src/Squidex.Infrastructure/Translations/DeepLTranslatorOptions.cs b/backend/src/Squidex.Infrastructure/Translations/DeepLTranslatorOptions.cs
deleted file mode 100644
index 653e3062d..000000000
--- a/backend/src/Squidex.Infrastructure/Translations/DeepLTranslatorOptions.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-// ==========================================================================
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex UG (haftungsbeschraenkt)
-// All rights reserved. Licensed under the MIT license.
-// ==========================================================================
-
-namespace Squidex.Infrastructure.Translations
-{
- public sealed class DeepLTranslatorOptions
- {
- public string? AuthKey { get; set; }
- }
-}
diff --git a/backend/src/Squidex.Infrastructure/Translations/ITranslator.cs b/backend/src/Squidex.Infrastructure/Translations/ITranslator.cs
deleted file mode 100644
index 84f6df5df..000000000
--- a/backend/src/Squidex.Infrastructure/Translations/ITranslator.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-// ==========================================================================
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex UG (haftungsbeschraenkt)
-// All rights reserved. Licensed under the MIT license.
-// ==========================================================================
-
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Squidex.Infrastructure.Translations
-{
- public interface ITranslator
- {
- Task Translate(string sourceText, Language targetLanguage, Language? sourceLanguage = null, CancellationToken ct = default);
- }
-}
diff --git a/backend/src/Squidex.Infrastructure/Translations/NoopTranslator.cs b/backend/src/Squidex.Infrastructure/Translations/NoopTranslator.cs
deleted file mode 100644
index 567d167fb..000000000
--- a/backend/src/Squidex.Infrastructure/Translations/NoopTranslator.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-// ==========================================================================
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex UG (haftungsbeschraenkt)
-// All rights reserved. Licensed under the MIT license.
-// ==========================================================================
-
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Squidex.Infrastructure.Translations
-{
- public sealed class NoopTranslator : ITranslator
- {
- public Task Translate(string sourceText, Language targetLanguage, Language? sourceLanguage = null, CancellationToken ct = default)
- {
- var result = new Translation(TranslationResult.NotImplemented);
-
- return Task.FromResult(result);
- }
- }
-}
diff --git a/backend/src/Squidex.Infrastructure/Translations/Translation.cs b/backend/src/Squidex.Infrastructure/Translations/Translation.cs
deleted file mode 100644
index 619259006..000000000
--- a/backend/src/Squidex.Infrastructure/Translations/Translation.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// ==========================================================================
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex UG (haftungsbeschraenkt)
-// All rights reserved. Licensed under the MIT license.
-// ==========================================================================
-
-namespace Squidex.Infrastructure.Translations
-{
- public sealed class Translation
- {
- public TranslationResult Result { get; }
-
- public string? Text { get; }
-
- public string? ResultText { get; set; }
-
- public Translation(TranslationResult result, string? text = null, string? resultText = null)
- {
- Text = text;
- Result = result;
- ResultText = resultText;
- }
- }
-}
diff --git a/backend/src/Squidex.Infrastructure/Translations/TranslationResult.cs b/backend/src/Squidex.Infrastructure/Translations/TranslationResult.cs
deleted file mode 100644
index f58f0ce45..000000000
--- a/backend/src/Squidex.Infrastructure/Translations/TranslationResult.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-// ==========================================================================
-// Squidex Headless CMS
-// ==========================================================================
-// Copyright (c) Squidex UG (haftungsbeschraenkt)
-// All rights reserved. Licensed under the MIT license.
-// ==========================================================================
-
-namespace Squidex.Infrastructure.Translations
-{
- public enum TranslationResult
- {
- Translated,
- LanguageNotSupported,
- NotTranslated,
- NotImplemented,
- Failed
- }
-}
diff --git a/backend/src/Squidex/Areas/Api/Controllers/Translations/Models/TranslationDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Translations/Models/TranslationDto.cs
index d2f528b90..8cf314fe0 100644
--- a/backend/src/Squidex/Areas/Api/Controllers/Translations/Models/TranslationDto.cs
+++ b/backend/src/Squidex/Areas/Api/Controllers/Translations/Models/TranslationDto.cs
@@ -7,6 +7,7 @@
using Squidex.Infrastructure.Reflection;
using Squidex.Infrastructure.Translations;
+using Squidex.Text.Translations;
namespace Squidex.Areas.Api.Controllers.Translations.Models
{
@@ -15,14 +16,14 @@ namespace Squidex.Areas.Api.Controllers.Translations.Models
///
/// The result of the translation.
///
- public TranslationResult Result { get; set; }
+ public TranslationResultCode Result { get; set; }
///
/// The translated text.
///
public string? Text { get; set; }
- public static TranslationDto FromTranslation(Translation translation)
+ public static TranslationDto FromTranslation(TranslationResult translation)
{
return SimpleMapper.Map(translation, new TranslationDto());
}
diff --git a/backend/src/Squidex/Areas/Api/Controllers/Translations/TranslationsController.cs b/backend/src/Squidex/Areas/Api/Controllers/Translations/TranslationsController.cs
index b4016457e..068f3e0ea 100644
--- a/backend/src/Squidex/Areas/Api/Controllers/Translations/TranslationsController.cs
+++ b/backend/src/Squidex/Areas/Api/Controllers/Translations/TranslationsController.cs
@@ -11,6 +11,7 @@ using Squidex.Areas.Api.Controllers.Translations.Models;
using Squidex.Infrastructure.Commands;
using Squidex.Infrastructure.Translations;
using Squidex.Shared;
+using Squidex.Text.Translations;
using Squidex.Web;
namespace Squidex.Areas.Api.Controllers.Translations
@@ -44,7 +45,7 @@ namespace Squidex.Areas.Api.Controllers.Translations
[ApiCosts(0)]
public async Task PostTranslation(string app, [FromBody] TranslateDto request)
{
- var result = await translator.Translate(request.Text, request.TargetLanguage, request.SourceLanguage, HttpContext.RequestAborted);
+ var result = await translator.TranslateAsync(request.Text, request.TargetLanguage, request.SourceLanguage, HttpContext.RequestAborted);
var response = TranslationDto.FromTranslation(result);
return Ok(response);
diff --git a/backend/src/Squidex/Config/Domain/InfrastructureServices.cs b/backend/src/Squidex/Config/Domain/InfrastructureServices.cs
index 948012389..efce25d96 100644
--- a/backend/src/Squidex/Config/Domain/InfrastructureServices.cs
+++ b/backend/src/Squidex/Config/Domain/InfrastructureServices.cs
@@ -9,6 +9,7 @@ using System;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
using NodaTime;
using Orleans;
using Squidex.Areas.Api.Controllers.Contents.Generator;
@@ -31,6 +32,8 @@ using Squidex.Infrastructure.Orleans;
using Squidex.Infrastructure.Translations;
using Squidex.Infrastructure.UsageTracking;
using Squidex.Pipeline.Robots;
+using Squidex.Text.Translations;
+using Squidex.Text.Translations.GoogleCloud;
using Squidex.Web;
using Squidex.Web.Pipeline;
@@ -120,15 +123,23 @@ namespace Squidex.Config.Domain
public static void AddSquidexTranslation(this IServiceCollection services, IConfiguration config)
{
- services.Configure(
+ services.Configure(
config.GetSection("translations:deepL"));
+ services.Configure(
+ config.GetSection("translations:googleCloud"));
services.Configure(
config.GetSection("languages"));
services.AddSingletonAs()
.AsSelf();
- services.AddSingletonAs()
+ services.AddSingletonAs(c => new DeepLTranslationService(c.GetRequiredService>().Value))
+ .As();
+
+ services.AddSingletonAs(c => new GoogleCloudTranslationService(c.GetRequiredService>().Value))
+ .As();
+
+ services.AddSingletonAs()
.As();
}
diff --git a/backend/src/Squidex/appsettings.json b/backend/src/Squidex/appsettings.json
index 1cbd48d94..df1358d62 100644
--- a/backend/src/Squidex/appsettings.json
+++ b/backend/src/Squidex/appsettings.json
@@ -672,11 +672,18 @@
},
"translations": {
- /*
- * The deepl api key if you want to support automated translations.
- */
"deepl": {
+ /*
+ * The deepl api key if you want to support automated translations.
+ */
"authKey": ""
+ },
+
+ "googleCloud": {
+ /*
+ * The google cloud project id if you want to support automated translations.
+ */
+ "projectId": ""
}
},
@@ -684,38 +691,38 @@
/*
* Set to true to rebuild apps.
*/
- "apps": false,
+ "apps": false,
- /*
+ /*
* Set to true to rebuild assets.
*/
- "assets": false,
+ "assets": false,
- /*
+ /*
* Set to true to create dummy asset files if they do not exist. Useful when a backup fail.
*/
- "assetFiles": false,
+ "assetFiles": false,
- /*
+ /*
* Set to true to rebuild contents.
*/
- "contents": false,
+ "contents": false,
- /*
+ /*
* Set to true to rebuild rules.
*/
- "rules": false,
+ "rules": false,
- /*
+ /*
* Set to true to rebuild schemas.
*/
- "schemas": false,
+ "schemas": false,
- /*
+ /*
* Set to true to rebuild indexes.
*/
- "indexes": false
- },
+ "indexes": false
+ },
/*"
* A list of configuration values that should be exposed from the info endpoint and in the UI.