diff --git a/text/Squidex.Text/Translations/DeepL/DeepLTranslationOptions.cs b/text/Squidex.Text/Translations/DeepL/DeepLTranslationOptions.cs index eb6f27c..26fe935 100644 --- a/text/Squidex.Text/Translations/DeepL/DeepLTranslationOptions.cs +++ b/text/Squidex.Text/Translations/DeepL/DeepLTranslationOptions.cs @@ -11,6 +11,12 @@ public sealed class DeepLTranslationOptions { public string AuthKey { get; set; } + public string? GlossaryById { get; set; } + + public string? GlossaryByName { get; set; } + + public string? TagHandling { get; set; } + public decimal CostsPerCharacterInEUR { get; set; } = 20m / 1_000_000; public Dictionary Mapping { get; set; } diff --git a/text/Squidex.Text/Translations/DeepL/DeepLTranslationService.cs b/text/Squidex.Text/Translations/DeepL/DeepLTranslationService.cs index cd0fc50..d78c5ff 100644 --- a/text/Squidex.Text/Translations/DeepL/DeepLTranslationService.cs +++ b/text/Squidex.Text/Translations/DeepL/DeepLTranslationService.cs @@ -5,6 +5,7 @@ // All rights reserved. Licensed under the MIT license. // ========================================================================== +using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Net; using System.Text.Json; @@ -19,6 +20,8 @@ public sealed class DeepLTranslationService(IHttpClientFactory httpClientFactory private const string UrlPaid = "https://api.deepl.com/v2/translate"; private const string UrlFree = "https://api-free.deepl.com/v2/translate"; private readonly DeepLTranslationOptions options = options.Value; + private readonly ConcurrentDictionary glossaryCache = new (); + private bool glossaryResolved; private sealed class TranslationsDto { @@ -35,6 +38,36 @@ private sealed class TranslationDto public string DetectedSourceLanguage { get; set; } } + private sealed class GlossariesDto + { + [JsonPropertyName("glossaries")] + public GlossaryDto[] Glossaries { get; set; } + } + + private sealed class GlossaryDto + { + [JsonPropertyName("glossary_id")] + public string GlossaryId { get; set; } // "def3a26b-3e84-45b3-84ae-0c0aaf3525f7" + + [JsonPropertyName("name")] + public string Name { get; set; } // "My Glossary" + + [JsonPropertyName("ready")] + public bool Ready { get; set; } // true + + [JsonPropertyName("source_lang")] + public string SourceLang { get; set; } // "en" + + [JsonPropertyName("target_lang")] + public string TargetLang { get; set; } // "de" + + [JsonPropertyName("creation_time")] + public DateTime CreationTime { get; set; } // "2021-08-03T14:16:18.329Z" + + [JsonPropertyName("entry_count")] + public int EntryCount { get; set; } // 1 + } + public bool IsConfigured { get; } = !string.IsNullOrWhiteSpace(options.Value.AuthKey); public async Task> TranslateAsync(IEnumerable texts, string targetLanguage, string? sourceLanguage = null, @@ -59,9 +92,12 @@ public async Task> TranslateAsync(IEnumerable> { - new KeyValuePair("target_lang", GetLanguageCode(targetLanguage)), + new KeyValuePair("target_lang", targetCode), }; foreach (var text in textsArray) @@ -69,9 +105,9 @@ public async Task> TranslateAsync(IEnumerable("text", text)); } - if (sourceLanguage != null) + if (sourceCode != null) { - parameters.Add(new KeyValuePair("source_lang", GetLanguageCode(sourceLanguage))); + parameters.Add(new KeyValuePair("source_lang", sourceCode)); } var url = @@ -80,6 +116,62 @@ public async Task> TranslateAsync(IEnumerable(glossaryJsonString)!; + + foreach (var glossary in glossaryJsonResponse.Glossaries) + { + if (options.GlossaryByName.Equals(glossary.Name, StringComparison.Ordinal) && glossary.Ready) + { + var key = $"{GetLanguageCode(glossary.SourceLang)}_{GetLanguageCode(glossary.TargetLang)}"; + glossaryCache[key] = glossary.GlossaryId; + } + } + + glossaryCache.TryGetValue(langPairKey, out glossaryId); + } + catch (Exception ex) + { + glossaryResolved = false; + AddError(TranslationResult.Failed(ex)); + } + } + } + + if (!string.IsNullOrWhiteSpace(glossaryId)) + { + parameters.Add(new KeyValuePair("glossary_id", glossaryId)); + } + + if (!string.IsNullOrWhiteSpace(options.TagHandling)) + { + parameters.Add(new KeyValuePair("tag_handling", options.TagHandling)); + } + using var httpRequest = new HttpRequestMessage(HttpMethod.Post, url) { Content = new FormUrlEncodedContent(parameters!), @@ -108,8 +200,14 @@ public async Task> TranslateAsync(IEnumerable