From 75fd2c6c062ef2c9ab145d953d7ede79f419fd07 Mon Sep 17 00:00:00 2001 From: Tim Cadenbach Date: Tue, 18 Apr 2023 14:02:13 +0200 Subject: [PATCH] Fix files, add sample and service --- samples/ASP.NET/ASP.NET.csproj | 14 + .../Controllers/TranslateController.cs | 23 + samples/ASP.NET/Program.cs | 46 + .../ASP.NET/Properties/launchSettings.json | 41 + samples/ASP.NET/appsettings.Development.json | 8 + samples/ASP.NET/appsettings.json | 9 + src/DeepL.Service/DeepL.Service.csproj | 13 + src/DeepL.Service/DeepLService.cs | 68 + src/DeepL.net.sln | 30 +- src/DeepL/GlossaryEntries.cs | 210 --- src/DeepL/Internal/DeepLClient.cs | 2 + src/DeepL/Internal/JsonUtils.cs | 1 + src/DeepL/LanguageCode.cs | 129 -- src/DeepL/Model/DocumentHandle.cs | 40 +- src/DeepL/Model/DocumentStatus.cs | 116 +- src/DeepL/Model/DocumentTranslateOptions.cs | 30 - .../Exceptions/DocumentNotReadyException.cs | 2 +- src/DeepL/Model/GlossaryEntries.cs | 210 +++ src/DeepL/Model/GlossaryInfo.cs | 104 +- src/DeepL/Model/GlossaryLanguagePair.cs | 50 +- src/DeepL/Model/Interfaces/ITranslator.cs | 401 +++++ src/DeepL/Model/Language.cs | 263 +-- src/DeepL/Model/LanguageCode.cs | 131 ++ .../Model/Options/DocumentTranslateOptions.cs | 28 + .../Model/Options/TextTranslateOptions.cs | 56 + src/DeepL/Model/Options/TranslatorOptions.cs | 96 + src/DeepL/Model/SentenceSplittingMode.cs | 24 + src/DeepL/Model/TextResult.cs | 51 +- src/DeepL/Model/Usage.cs | 175 +- src/DeepL/SentenceSplittingMode.cs | 23 - src/DeepL/TextTranslateOptions.cs | 56 - src/DeepL/Translator.cs | 1586 +++++++---------- src/DeepL/TranslatorOptions.cs | 89 - src/DeepLTests/BaseDeepLTest.cs | 2 + src/DeepLTests/GeneralTest.cs | 2 + src/DeepLTests/GlossaryTest.cs | 1 + src/DeepLTests/SubstitutionExamples.cs | 3 +- src/DeepLTests/TranslateDocumentTest.cs | 1 + src/DeepLTests/TranslateTextTest.cs | 1 + 39 files changed, 2211 insertions(+), 1924 deletions(-) create mode 100644 samples/ASP.NET/ASP.NET.csproj create mode 100644 samples/ASP.NET/Controllers/TranslateController.cs create mode 100644 samples/ASP.NET/Program.cs create mode 100644 samples/ASP.NET/Properties/launchSettings.json create mode 100644 samples/ASP.NET/appsettings.Development.json create mode 100644 samples/ASP.NET/appsettings.json create mode 100644 src/DeepL.Service/DeepL.Service.csproj create mode 100644 src/DeepL.Service/DeepLService.cs delete mode 100644 src/DeepL/GlossaryEntries.cs delete mode 100644 src/DeepL/LanguageCode.cs delete mode 100644 src/DeepL/Model/DocumentTranslateOptions.cs create mode 100644 src/DeepL/Model/GlossaryEntries.cs create mode 100644 src/DeepL/Model/Interfaces/ITranslator.cs create mode 100644 src/DeepL/Model/LanguageCode.cs create mode 100644 src/DeepL/Model/Options/DocumentTranslateOptions.cs create mode 100644 src/DeepL/Model/Options/TextTranslateOptions.cs create mode 100644 src/DeepL/Model/Options/TranslatorOptions.cs create mode 100644 src/DeepL/Model/SentenceSplittingMode.cs delete mode 100644 src/DeepL/SentenceSplittingMode.cs delete mode 100644 src/DeepL/TextTranslateOptions.cs delete mode 100644 src/DeepL/TranslatorOptions.cs diff --git a/samples/ASP.NET/ASP.NET.csproj b/samples/ASP.NET/ASP.NET.csproj new file mode 100644 index 0000000..4e45aa3 --- /dev/null +++ b/samples/ASP.NET/ASP.NET.csproj @@ -0,0 +1,14 @@ + + + + net7.0 + enable + enable + + + + + + + + diff --git a/samples/ASP.NET/Controllers/TranslateController.cs b/samples/ASP.NET/Controllers/TranslateController.cs new file mode 100644 index 0000000..4c161c5 --- /dev/null +++ b/samples/ASP.NET/Controllers/TranslateController.cs @@ -0,0 +1,23 @@ +// Copyright 2022 DeepL SE (https://www.deepl.com) +// Use of this source code is governed by an MIT +// license that can be found in the LICENSE file. + +using DeepL.Service; +using Microsoft.AspNetCore.Mvc; + +namespace ASP.NET.Controllers; + +public class TranslateController : Controller { + + private readonly DeepLService deepl; + + public TranslateController(DeepLService deepLService) { + this.deepl = deepLService; + } + + [HttpGet] + public async Task Index(string translateMe, string targetLanguage) { + var result = await deepl.client.TranslateTextAsync(translateMe, string.Empty, targetLanguage); + return Ok(result.Text); + } +} diff --git a/samples/ASP.NET/Program.cs b/samples/ASP.NET/Program.cs new file mode 100644 index 0000000..2602a66 --- /dev/null +++ b/samples/ASP.NET/Program.cs @@ -0,0 +1,46 @@ +// Copyright 2022 DeepL SE (https://www.deepl.com) +// Use of this source code is governed by an MIT +// license that can be found in the LICENSE file. + +using DeepL.Model.Options; +using DeepL.Service; + +namespace ASP.NET; + +public class Program { + public static void Main(string[] args) { + var builder = WebApplication.CreateBuilder(args); + + // Add services to the container. + + builder.Services.AddControllers(); + + // Add DeepL Services + builder.Services.AddDeepL(options => { + options.ServiceLifetime = ServiceLifetime.Singleton; + options.ApiKey = "test"; + options.TranslatorOptions = new TranslatorOptions() { + appInfo = new AppInfo() { + AppName = "test", + AppVersion = "1.0.0", + } + }; + }); + + + + + var app = builder.Build(); + + // Configure the HTTP request pipeline. + + app.UseHttpsRedirection(); + + app.UseAuthorization(); + + + app.MapControllers(); + + app.Run(); + } +} diff --git a/samples/ASP.NET/Properties/launchSettings.json b/samples/ASP.NET/Properties/launchSettings.json new file mode 100644 index 0000000..b26ed26 --- /dev/null +++ b/samples/ASP.NET/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:41365", + "sslPort": 44320 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "weatherforecast", + "applicationUrl": "http://localhost:5030", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "weatherforecast", + "applicationUrl": "https://localhost:7011;http://localhost:5030", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "weatherforecast", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/ASP.NET/appsettings.Development.json b/samples/ASP.NET/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/samples/ASP.NET/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/samples/ASP.NET/appsettings.json b/samples/ASP.NET/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/samples/ASP.NET/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/DeepL.Service/DeepL.Service.csproj b/src/DeepL.Service/DeepL.Service.csproj new file mode 100644 index 0000000..69786b3 --- /dev/null +++ b/src/DeepL.Service/DeepL.Service.csproj @@ -0,0 +1,13 @@ + + + + net7.0 + enable + enable + + + + + + + diff --git a/src/DeepL.Service/DeepLService.cs b/src/DeepL.Service/DeepLService.cs new file mode 100644 index 0000000..8b36c95 --- /dev/null +++ b/src/DeepL.Service/DeepLService.cs @@ -0,0 +1,68 @@ +// Copyright 2022 DeepL SE (https://www.deepl.com) +// Use of this source code is governed by an MIT +// license that can be found in the LICENSE file. + +using DeepL.Model.Interfaces; +using DeepL.Model.Options; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace DeepL.Service; + +public class DeepLConfiguration { + + public TranslatorOptions TranslatorOptions { get; set; } + public ServiceLifetime ServiceLifetime { get; set; } = ServiceLifetime.Scoped; + public string ApiKey { get; set; } + + public Type? customTranslator { get; set; } = null; + +} + +public class DeepLService { + + public Translator client { get; set; } + + public DeepLService(IOptions options) { + if(options.Value == null) throw new ArgumentNullException(nameof(options.Value)); + this.client = new Translator(options.Value.ApiKey, options.Value.TranslatorOptions); + } +} + + +public static class DeepLExtension { + + /// + /// Adds DeepL to DI + /// + /// Service Collection + /// Service Lifetime + /// Translator Options + /// + public static IServiceCollection AddDeepL(this IServiceCollection services, Action options) { + + if (services == null) throw new ArgumentNullException(nameof(services)); + if (options == null) throw new ArgumentNullException(nameof(options)); + + //Apply Config + services.AddOptions(); + services.Configure(options); + + var deeplConfig = new DeepLConfiguration(); + options?.Invoke(deeplConfig); + + // Lets see if we want to use a custom translator + var translatorType = typeof(Translator); + if (deeplConfig.customTranslator != null) { + if (deeplConfig.customTranslator.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITranslator))) { + translatorType = typeof(Translator); + } else { + throw new ArgumentException("CustomTranslator must implement ITranslator Interface"); + } + } + + services.Add(new ServiceDescriptor(typeof(ITranslator), translatorType, deeplConfig.ServiceLifetime)); + return services; + } +} + diff --git a/src/DeepL.net.sln b/src/DeepL.net.sln index 34992b7..c96fd05 100644 --- a/src/DeepL.net.sln +++ b/src/DeepL.net.sln @@ -1,8 +1,17 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepL", "DeepL\DeepL.csproj", "{87DDF1B7-2007-4E90-BDF3-6F6AE465A853}" +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33513.286 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeepL", "DeepL\DeepL.csproj", "{87DDF1B7-2007-4E90-BDF3-6F6AE465A853}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepLTests", "DeepLTests\DeepLTests.csproj", "{3582DAA3-A216-4D58-83C8-BA91B4EE2260}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeepLTests", "DeepLTests\DeepLTests.csproj", "{3582DAA3-A216-4D58-83C8-BA91B4EE2260}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeepL.Service", "DeepL.Service\DeepL.Service.csproj", "{C649CF0E-AE41-4964-A958-4D70FDFB5CDE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASP.NET", "..\samples\ASP.NET\ASP.NET.csproj", "{4EB6C377-EBBA-4C62-9D71-4EFED283D7A2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{B4D0D020-6F5B-4C38-BE54-6CD8F1DA2971}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -18,5 +27,22 @@ Global {3582DAA3-A216-4D58-83C8-BA91B4EE2260}.Debug|Any CPU.Build.0 = Debug|Any CPU {3582DAA3-A216-4D58-83C8-BA91B4EE2260}.Release|Any CPU.ActiveCfg = Release|Any CPU {3582DAA3-A216-4D58-83C8-BA91B4EE2260}.Release|Any CPU.Build.0 = Release|Any CPU + {C649CF0E-AE41-4964-A958-4D70FDFB5CDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C649CF0E-AE41-4964-A958-4D70FDFB5CDE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C649CF0E-AE41-4964-A958-4D70FDFB5CDE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C649CF0E-AE41-4964-A958-4D70FDFB5CDE}.Release|Any CPU.Build.0 = Release|Any CPU + {4EB6C377-EBBA-4C62-9D71-4EFED283D7A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4EB6C377-EBBA-4C62-9D71-4EFED283D7A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4EB6C377-EBBA-4C62-9D71-4EFED283D7A2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4EB6C377-EBBA-4C62-9D71-4EFED283D7A2}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {4EB6C377-EBBA-4C62-9D71-4EFED283D7A2} = {B4D0D020-6F5B-4C38-BE54-6CD8F1DA2971} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CC5D0C9B-8F46-46B0-9877-CA22C51A5ABB} EndGlobalSection EndGlobal diff --git a/src/DeepL/GlossaryEntries.cs b/src/DeepL/GlossaryEntries.cs deleted file mode 100644 index b4a3a14..0000000 --- a/src/DeepL/GlossaryEntries.cs +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2022 DeepL SE (https://www.deepl.com) -// Use of this source code is governed by an MIT -// license that can be found in the LICENSE file. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace DeepL { - /// Stores the entries of a glossary. - public sealed class GlossaryEntries { - private const char TermSeparator = '\t'; - private static readonly string[] LineSeparators = { "\r\n", "\n", "\r" }; - private readonly string _contentTsv; - - /// - /// Initializes a new object using the given source-target entry pairs. - /// - /// Source-target entry pairs to convert. - /// If true, validity checks on the entries are skipped, defaults to false. - /// String containing the entries in TSV format. - /// If the entries fail any validity check. - public GlossaryEntries( - IEnumerable> entryPairs, - bool skipChecks = false) { - _contentTsv = ConvertToTsv(entryPairs.Select(pair => (pair.Key, pair.Value)), skipChecks); - } - - /// - /// Initializes a new object using the given source-target entry pairs. - /// - /// Source-target entry pairs to convert. - /// If true, validity checks on the entries are skipped, defaults to false. - /// String containing the entries in TSV format. - /// If the entries fail any validity check. - public GlossaryEntries( - IEnumerable<(string Key, string Value)> entryPairs, - bool skipChecks = false) { - _contentTsv = ConvertToTsv(entryPairs, skipChecks); - } - - /// - /// Converts the given tab-separated-value (TSV) string of glossary entries into a new . - /// Whitespace is trimmed from the start and end of each term. - /// - /// String containing the entries in TSV format. - /// If true, validity checks on the entries are skipped, defaults to false. - /// containing the source-target entry pairs. - /// If the entries fail any validity check. - private GlossaryEntries(string contentTsv, bool skipChecks = false) { - _contentTsv = contentTsv; - if (!skipChecks) { - // ToDictionary() validates the TSV string - var _ = ToDictionary(contentTsv); - // Result is intentionally ignored - } - } - - /// - /// Converts the given tab-separated-value (TSV) string of glossary entries into a new . - /// Whitespace is trimmed from the start and end of each term. - /// - /// String containing the entries in TSV format. - /// If true, validity checks on the entries are skipped, defaults to false. - /// containing the source-target entry pairs. - /// If the entries fail any validity check. - public static GlossaryEntries FromTsv(string contentTsv, bool skipChecks = false) => - new GlossaryEntries(contentTsv, skipChecks); - - /// - /// Converts the glossary entry list into a dictionary of source-target entry pairs. Whitespace is trimmed from the start - /// and end of each term. - /// - /// If true, validity checks on the entries are skipped, defaults to false. - /// Dictionary containing the source-target entry pairs. - public Dictionary ToDictionary(bool skipChecks = false) => - ToDictionary(_contentTsv, skipChecks); - - /// Converts the to a tab-separated-value (TSV) string. - /// TSV format string containing glossary entries. - public string ToTsv() => _contentTsv; - - /// Converts the to a string containing the source-target entries. - /// A string containing the source-target entries. - /// - /// This function is for diagnostic purposes only; the content of the returned string is exempt from backwards - /// compatibility. - /// - public override string ToString() => - nameof(GlossaryEntries) + "[" + - string.Join(", ", ToDictionary().Select(pair => $"[\"{pair.Key}\", \"{pair.Value}\"]")) + "]"; - - /// - /// Checks the validity of the given glossary term, for example that it contains no invalid characters. Whitespace - /// at the start and end of the term is ignored. - /// - /// String containing term to check. - /// If the term is invalid. - /// - /// Terms are considered valid if they comprise at least one non-whitespace character, and contain no invalid - /// characters: C0 and C1 control characters, and Unicode newlines. - /// - public static void ValidateGlossaryTerm(string term) { - var termTrimmed = term.Trim(); - - if (termTrimmed.Length == 0) { - throw new ArgumentException($"Argument {nameof(term)} \"{term}\" contains no non-whitespace characters"); - } - - foreach (var ch in termTrimmed) { - if ((0 <= ch && ch <= 31) || // C0 control characters - (128 <= ch && ch <= 159) || // C1 control characters - ch == '\u2028' || ch == '\u2029' // Unicode newlines - ) { - throw new ArgumentException( - $"Argument {nameof(term)} \"{term}\" contains invalid character: '{ch}' (U+{Convert.ToInt32(ch):X4})"); - } - } - } - - /// - /// Converts the given source-target entry pairs to a string containing the entries in tab-separated-value (TSV) - /// format. - /// - /// IEnumerable of source-target entry pairs to convert. - /// If true, validity checks on the entries are skipped, defaults to false. - /// String containing the entries in TSV format. - private static string ConvertToTsv( - IEnumerable<(string Key, string Value)> entries, - bool skipChecks = false) { - var builder = new StringBuilder(); - var dictionary = skipChecks ? null : new Dictionary(); - foreach (var pair in entries) { - var source = pair.Key.Trim(); - var target = pair.Value.Trim(); - if (!skipChecks) { - ValidateGlossaryTerm(source); - ValidateGlossaryTerm(target); - if (dictionary!.ContainsKey(source)) { - throw new ArgumentException($"{nameof(entries)} contains duplicate source term: \"{source}\""); - } - - dictionary.Add(source, target); - } - - if (builder.Length > 0) { - builder.Append("\n"); - } - - builder.Append($"{source}\t{target}"); - } - - if (builder.Length == 0) { - throw new ArgumentException($"{nameof(entries)} contains no entries"); - } - - return builder.ToString(); - } - - /// - /// Converts the given TSV string into a dictionary of source-target entry pairs. Whitespace is trimmed from the start - /// and end of each term. - /// - /// String containing the entries in TSV format. - /// If true, validity checks on the entries are skipped, defaults to false. - /// Dictionary containing the source-target entry pairs. - private static Dictionary ToDictionary( - string contentTsv, - bool skipChecks = false) { - var entries = new Dictionary(); - var lineNumber = 0; - foreach (var line in contentTsv.Split(LineSeparators, StringSplitOptions.None)) { - lineNumber += 1; - var lineTrimmed = line.Trim(); - if (lineTrimmed.Length == 0) { - continue; - } - - var termSeparatorPos = lineTrimmed.IndexOf(TermSeparator); - if (termSeparatorPos == -1) { - throw new ArgumentException($"Entry on line {lineNumber} does not contain term separator: {line}"); - } - - var source = lineTrimmed.Substring(0, termSeparatorPos).Trim(); - var target = lineTrimmed.Substring(termSeparatorPos + 1).Trim(); - if (!skipChecks) { - if (target.Contains(TermSeparator)) { - throw new ArgumentException($"Entry on line {lineNumber} contains more than one term separator: {line}"); - } - - ValidateGlossaryTerm(source); - ValidateGlossaryTerm(target); - } - - try { - entries.Add(source, target); - } catch (ArgumentException exception) { - throw new ArgumentException($"Entry on line {lineNumber} duplicates source term \"{source}\"", exception); - } - } - - if (!skipChecks && entries.Count == 0) { - throw new ArgumentException($"Argument {nameof(contentTsv)} contains no TSV entries"); - } - - return entries; - } - } -} diff --git a/src/DeepL/Internal/DeepLClient.cs b/src/DeepL/Internal/DeepLClient.cs index 107b7df..cbeedb9 100644 --- a/src/DeepL/Internal/DeepLClient.cs +++ b/src/DeepL/Internal/DeepLClient.cs @@ -13,6 +13,8 @@ using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; +using DeepL.Model.Exceptions; +using DeepL.Model.Options; using Microsoft.Extensions.Http; using Polly; using Polly.Timeout; diff --git a/src/DeepL/Internal/JsonUtils.cs b/src/DeepL/Internal/JsonUtils.cs index 9a3532b..1244c72 100644 --- a/src/DeepL/Internal/JsonUtils.cs +++ b/src/DeepL/Internal/JsonUtils.cs @@ -7,6 +7,7 @@ using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; +using DeepL.Model.Exceptions; namespace DeepL.Internal; diff --git a/src/DeepL/LanguageCode.cs b/src/DeepL/LanguageCode.cs deleted file mode 100644 index d77fed1..0000000 --- a/src/DeepL/LanguageCode.cs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2022 DeepL SE (https://www.deepl.com) -// Use of this source code is governed by an MIT -// license that can be found in the LICENSE file. - -namespace DeepL { - /// - /// Language codes for the languages currently supported by DeepL translation. New languages may be added in - /// future; to retrieve the currently supported languages use and - /// . - /// - public static class LanguageCode { - /// Bulgarian language code, may be used as source or target language. - public const string Bulgarian = "bg"; - - /// Czech language code, may be used as source or target language. - public const string Czech = "cs"; - - /// Danish language code, may be used as source or target language. - public const string Danish = "da"; - - /// German language code, may be used as source or target language. - public const string German = "de"; - - /// Greek language code, may be used as source or target language. - public const string Greek = "el"; - - /// English language code, may only be used as a source language. - public const string English = "en"; - - /// British English language code, may only be used as a target language. - public const string EnglishBritish = "en-GB"; - - /// American English language code, may only be used as a target language. - public const string EnglishAmerican = "en-US"; - - /// Spanish language code, may be used as source or target language. - public const string Spanish = "es"; - - /// Estonian language code, may be used as source or target language. - public const string Estonian = "et"; - - /// Finnish language code, may be used as source or target language. - public const string Finnish = "fi"; - - /// French language code, may be used as source or target language. - public const string French = "fr"; - - /// Hungarian language code, may be used as source or target language. - public const string Hungarian = "hu"; - - /// Indonesian language code, may be used as source or target language. - public const string Indonesian = "id"; - - /// Italian language code, may be used as source or target language. - public const string Italian = "it"; - - /// Japanese language code, may be used as source or target language. - public const string Japanese = "ja"; - - /// Korean language code, may be used as source or target language. - public const string Korean = "ko"; - - /// Lithuanian language code, may be used as source or target language. - public const string Lithuanian = "lt"; - - /// Latvian language code, may be used as source or target language. - public const string Latvian = "lv"; - - /// Norwegian (bokmål) language code, may be used as source or target language. - public const string Norwegian = "nb"; - - /// Dutch language code, may be used as source or target language. - public const string Dutch = "nl"; - - /// Polish language code, may be used as source or target language. - public const string Polish = "pl"; - - /// Portuguese language code, may only be used as a source language. - public const string Portuguese = "pt"; - - /// Brazilian Portuguese language code, may only be used as a target language. - public const string PortugueseBrazilian = "pt-BR"; - - /// European Portuguese language code, may only be used as a target language. - public const string PortugueseEuropean = "pt-PT"; - - /// Romanian language code, may be used as source or target language. - public const string Romanian = "ro"; - - /// Russian language code, may be used as source or target language. - public const string Russian = "ru"; - - /// Slovak language code, may be used as source or target language. - public const string Slovak = "sk"; - - /// Slovenian language code, may be used as source or target language. - public const string Slovenian = "sl"; - - /// Swedish language code, may be used as source or target language. - public const string Swedish = "sv"; - - /// Turkish language code, may be used as source or target language. - public const string Turkish = "tr"; - - /// Ukrainian language code, may be used as source or target language. - public const string Ukrainian = "uk"; - - /// Chinese language code, may be used as source or target language. - public const string Chinese = "zh"; - - /// Removes the regional variant (if any) from the given language code - /// Language code possibly containing a regional variant. - /// The language code without a regional variant. - public static string RemoveRegionalVariant(string code) => code.Split(new[] { '-' }, 2)[0].ToLowerInvariant(); - - /// - /// Changes the upper- and lower-casing of the given language code to match ISO 639-1 with an optional regional - /// code from ISO 3166-1. - /// - /// String containing language code to standardize. - /// String containing the standardized language code. - internal static string Standardize(string code) { - var parts = code.Split(new[] { '-' }, 2); - return parts.Length == 1 - ? parts[0].ToLowerInvariant() - : $"{parts[0].ToLowerInvariant()}-{parts[1].ToUpperInvariant()}"; - } - } -} diff --git a/src/DeepL/Model/DocumentHandle.cs b/src/DeepL/Model/DocumentHandle.cs index a3ceb51..3334687 100644 --- a/src/DeepL/Model/DocumentHandle.cs +++ b/src/DeepL/Model/DocumentHandle.cs @@ -4,26 +4,28 @@ using System.Text.Json.Serialization; -namespace DeepL.Model { - /// Handle to an in-progress document translation. - public readonly struct DocumentHandle { - /// Initializes a new object to resume an in-progress document translation. - /// Document ID returned by document upload. - /// Document Key returned by document upload. - /// - /// The constructor for this class (and all other Model classes) should not be used by library users. Ideally it - /// would be marked , but needs to be for JSON deserialization. - /// In future this function may have backwards-incompatible changes. - /// - [JsonConstructor] - public DocumentHandle(string documentId, string documentKey) { - (DocumentId, DocumentKey) = (documentId, documentKey); - } +namespace DeepL.Model; - /// ID of associated document request. - public string DocumentId { get; } +/// Handle to an in-progress document translation. +public readonly struct DocumentHandle { - /// Key of associated document request. - public string DocumentKey { get; } + + /// Initializes a new object to resume an in-progress document translation. + /// Document ID returned by document upload. + /// Document Key returned by document upload. + /// + /// The constructor for this class (and all other Model classes) should not be used by library users. Ideally it + /// would be marked , but needs to be for JSON deserialization. + /// In future this function may have backwards-incompatible changes. + /// + [JsonConstructor] + public DocumentHandle(string documentId, string documentKey) { + (DocumentId, DocumentKey) = (documentId, documentKey); } + + /// ID of associated document request. + public string DocumentId { get; } + + /// Key of associated document request. + public string DocumentKey { get; } } diff --git a/src/DeepL/Model/DocumentStatus.cs b/src/DeepL/Model/DocumentStatus.cs index 0a3ef35..9de4c9d 100644 --- a/src/DeepL/Model/DocumentStatus.cs +++ b/src/DeepL/Model/DocumentStatus.cs @@ -5,73 +5,73 @@ using System.Runtime.Serialization; using System.Text.Json.Serialization; -namespace DeepL.Model { - /// Status of an in-progress document translation. - public sealed class DocumentStatus { - /// Status code indicating status of the document translation. - [JsonConverter(typeof(JsonStringEnumConverter))] - public enum StatusCode { - /// Document translation has not yet started, but will begin soon. - [EnumMember(Value = "queued")] Queued, +namespace DeepL.Model; - /// Document translation is in progress. - [EnumMember(Value = "translating")] Translating, +/// Status of an in-progress document translation. +public sealed class DocumentStatus { + /// Status code indicating status of the document translation. + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum StatusCode { + /// Document translation has not yet started, but will begin soon. + [EnumMember(Value = "queued")] Queued, - /// Document translation completed successfully, and the translated document may be downloaded. - [EnumMember(Value = "done")] Done, + /// Document translation is in progress. + [EnumMember(Value = "translating")] Translating, - /// An error occurred during document translation. - [EnumMember(Value = "error")] Error - } + /// Document translation completed successfully, and the translated document may be downloaded. + [EnumMember(Value = "done")] Done, - /// Initializes a new object for an in-progress document translation. - /// Document ID of the associated document. - /// Status of the document translation. - /// - /// Number of seconds remaining until translation is complete, only included while - /// document is in translating state. - /// - /// - /// Number of characters billed for the translation of this document, only included - /// after document translation is finished and the state is done. - /// - /// Short description of the error, if available. - /// - /// The constructor for this class (and all other Model classes) should not be used by library users. Ideally it - /// would be marked , but needs to be for JSON deserialization. - /// In future this function may have backwards-incompatible changes. - /// - [JsonConstructor] - public DocumentStatus(string documentId, StatusCode status, int? secondsRemaining, int? billedCharacters, string? errorMessage) { - (DocumentId, Status, SecondsRemaining, BilledCharacters, ErrorMessage) = - (documentId, status, secondsRemaining, billedCharacters, errorMessage); - } + /// An error occurred during document translation. + [EnumMember(Value = "error")] Error + } + + /// Initializes a new object for an in-progress document translation. + /// Document ID of the associated document. + /// Status of the document translation. + /// + /// Number of seconds remaining until translation is complete, only included while + /// document is in translating state. + /// + /// + /// Number of characters billed for the translation of this document, only included + /// after document translation is finished and the state is done. + /// + /// Short description of the error, if available. + /// + /// The constructor for this class (and all other Model classes) should not be used by library users. Ideally it + /// would be marked , but needs to be for JSON deserialization. + /// In future this function may have backwards-incompatible changes. + /// + [JsonConstructor] + public DocumentStatus(string documentId, StatusCode status, int? secondsRemaining, int? billedCharacters, string? errorMessage) { + (DocumentId, Status, SecondsRemaining, BilledCharacters, ErrorMessage) = + (documentId, status, secondsRemaining, billedCharacters, errorMessage); + } - /// Document ID of the associated document. - public string DocumentId { get; } + /// Document ID of the associated document. + public string DocumentId { get; } - /// Status of the document translation. - public StatusCode Status { get; } + /// Status of the document translation. + public StatusCode Status { get; } - /// - /// Number of seconds remaining until translation is complete, only included while - /// document is in translating state. - /// - public int? SecondsRemaining { get; } + /// + /// Number of seconds remaining until translation is complete, only included while + /// document is in translating state. + /// + public int? SecondsRemaining { get; } - /// - /// Number of characters billed for the translation of this document, only included - /// after document translation is finished and the state is done. - /// - public int? BilledCharacters { get; } + /// + /// Number of characters billed for the translation of this document, only included + /// after document translation is finished and the state is done. + /// + public int? BilledCharacters { get; } - /// Short description of the error, if available. - public string? ErrorMessage { get; } + /// Short description of the error, if available. + public string? ErrorMessage { get; } - /// true if no error has occurred during document translation, otherwise false. - public bool Ok => Status != StatusCode.Error; + /// true if no error has occurred during document translation, otherwise false. + public bool Ok => Status != StatusCode.Error; - /// true if document translation has completed successfully, otherwise false. - public bool Done => Status == StatusCode.Done; - } + /// true if document translation has completed successfully, otherwise false. + public bool Done => Status == StatusCode.Done; } diff --git a/src/DeepL/Model/DocumentTranslateOptions.cs b/src/DeepL/Model/DocumentTranslateOptions.cs deleted file mode 100644 index d24101e..0000000 --- a/src/DeepL/Model/DocumentTranslateOptions.cs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2022 DeepL SE (https://www.deepl.com) -// Use of this source code is governed by an MIT -// license that can be found in the LICENSE file. - -using DeepL.Model; - -namespace DeepL { - /// - /// Options to control document translation behaviour. These options may be provided to - /// document translate functions. - /// - public sealed class DocumentTranslateOptions { - /// Initializes a new object. - public DocumentTranslateOptions() { } - - /// Initializes a new object including the given glossary. - /// Glossary to use in translation. - public DocumentTranslateOptions(GlossaryInfo glossary) : this() { - GlossaryId = glossary.GlossaryId; - } - - /// Controls whether translations should lean toward formal or informal language. - /// This option is only applicable for target languages that support the formality option. - /// - public Formality Formality { get; set; } = Formality.Default; - - /// Specifies the ID of a glossary to use with the translation. - public string? GlossaryId { get; set; } - } -} diff --git a/src/DeepL/Model/Exceptions/DocumentNotReadyException.cs b/src/DeepL/Model/Exceptions/DocumentNotReadyException.cs index 4648092..c5cb2ac 100644 --- a/src/DeepL/Model/Exceptions/DocumentNotReadyException.cs +++ b/src/DeepL/Model/Exceptions/DocumentNotReadyException.cs @@ -1,4 +1,4 @@ -// Copyright 2022 DeepL SE (https://www.deepl.com) +// Copyright 2022 DeepL SE (https://www.deepl.com) // Use of this source code is governed by an MIT // license that can be found in the LICENSE file. diff --git a/src/DeepL/Model/GlossaryEntries.cs b/src/DeepL/Model/GlossaryEntries.cs new file mode 100644 index 0000000..6cacdbb --- /dev/null +++ b/src/DeepL/Model/GlossaryEntries.cs @@ -0,0 +1,210 @@ +// Copyright 2022 DeepL SE (https://www.deepl.com) +// Use of this source code is governed by an MIT +// license that can be found in the LICENSE file. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace DeepL.Model; + +/// Stores the entries of a glossary. +public sealed class GlossaryEntries { + private const char TermSeparator = '\t'; + private static readonly string[] LineSeparators = { "\r\n", "\n", "\r" }; + private readonly string _contentTsv; + + /// + /// Initializes a new object using the given source-target entry pairs. + /// + /// Source-target entry pairs to convert. + /// If true, validity checks on the entries are skipped, defaults to false. + /// String containing the entries in TSV format. + /// If the entries fail any validity check. + public GlossaryEntries( + IEnumerable> entryPairs, + bool skipChecks = false) { + _contentTsv = ConvertToTsv(entryPairs.Select(pair => (pair.Key, pair.Value)), skipChecks); + } + + /// + /// Initializes a new object using the given source-target entry pairs. + /// + /// Source-target entry pairs to convert. + /// If true, validity checks on the entries are skipped, defaults to false. + /// String containing the entries in TSV format. + /// If the entries fail any validity check. + public GlossaryEntries( + IEnumerable<(string Key, string Value)> entryPairs, + bool skipChecks = false) { + _contentTsv = ConvertToTsv(entryPairs, skipChecks); + } + + /// + /// Converts the given tab-separated-value (TSV) string of glossary entries into a new . + /// Whitespace is trimmed from the start and end of each term. + /// + /// String containing the entries in TSV format. + /// If true, validity checks on the entries are skipped, defaults to false. + /// containing the source-target entry pairs. + /// If the entries fail any validity check. + private GlossaryEntries(string contentTsv, bool skipChecks = false) { + _contentTsv = contentTsv; + if (!skipChecks) { + // ToDictionary() validates the TSV string + var _ = ToDictionary(contentTsv); + // Result is intentionally ignored + } + } + + /// + /// Converts the given tab-separated-value (TSV) string of glossary entries into a new . + /// Whitespace is trimmed from the start and end of each term. + /// + /// String containing the entries in TSV format. + /// If true, validity checks on the entries are skipped, defaults to false. + /// containing the source-target entry pairs. + /// If the entries fail any validity check. + public static GlossaryEntries FromTsv(string contentTsv, bool skipChecks = false) => + new GlossaryEntries(contentTsv, skipChecks); + + /// + /// Converts the glossary entry list into a dictionary of source-target entry pairs. Whitespace is trimmed from the start + /// and end of each term. + /// + /// If true, validity checks on the entries are skipped, defaults to false. + /// Dictionary containing the source-target entry pairs. + public Dictionary ToDictionary(bool skipChecks = false) => + ToDictionary(_contentTsv, skipChecks); + + /// Converts the to a tab-separated-value (TSV) string. + /// TSV format string containing glossary entries. + public string ToTsv() => _contentTsv; + + /// Converts the to a string containing the source-target entries. + /// A string containing the source-target entries. + /// + /// This function is for diagnostic purposes only; the content of the returned string is exempt from backwards + /// compatibility. + /// + public override string ToString() => + nameof(GlossaryEntries) + "[" + + string.Join(", ", ToDictionary().Select(pair => $"[\"{pair.Key}\", \"{pair.Value}\"]")) + "]"; + + /// + /// Checks the validity of the given glossary term, for example that it contains no invalid characters. Whitespace + /// at the start and end of the term is ignored. + /// + /// String containing term to check. + /// If the term is invalid. + /// + /// Terms are considered valid if they comprise at least one non-whitespace character, and contain no invalid + /// characters: C0 and C1 control characters, and Unicode newlines. + /// + public static void ValidateGlossaryTerm(string term) { + var termTrimmed = term.Trim(); + + if (termTrimmed.Length == 0) { + throw new ArgumentException($"Argument {nameof(term)} \"{term}\" contains no non-whitespace characters"); + } + + foreach (var ch in termTrimmed) { + if (0 <= ch && ch <= 31 || // C0 control characters + 128 <= ch && ch <= 159 || // C1 control characters + ch == '\u2028' || ch == '\u2029' // Unicode newlines + ) { + throw new ArgumentException( + $"Argument {nameof(term)} \"{term}\" contains invalid character: '{ch}' (U+{Convert.ToInt32(ch):X4})"); + } + } + } + + /// + /// Converts the given source-target entry pairs to a string containing the entries in tab-separated-value (TSV) + /// format. + /// + /// IEnumerable of source-target entry pairs to convert. + /// If true, validity checks on the entries are skipped, defaults to false. + /// String containing the entries in TSV format. + private static string ConvertToTsv( + IEnumerable<(string Key, string Value)> entries, + bool skipChecks = false) { + var builder = new StringBuilder(); + var dictionary = skipChecks ? null : new Dictionary(); + foreach (var pair in entries) { + var source = pair.Key.Trim(); + var target = pair.Value.Trim(); + if (!skipChecks) { + ValidateGlossaryTerm(source); + ValidateGlossaryTerm(target); + if (dictionary!.ContainsKey(source)) { + throw new ArgumentException($"{nameof(entries)} contains duplicate source term: \"{source}\""); + } + + dictionary.Add(source, target); + } + + if (builder.Length > 0) { + builder.Append("\n"); + } + + builder.Append($"{source}\t{target}"); + } + + if (builder.Length == 0) { + throw new ArgumentException($"{nameof(entries)} contains no entries"); + } + + return builder.ToString(); + } + + /// + /// Converts the given TSV string into a dictionary of source-target entry pairs. Whitespace is trimmed from the start + /// and end of each term. + /// + /// String containing the entries in TSV format. + /// If true, validity checks on the entries are skipped, defaults to false. + /// Dictionary containing the source-target entry pairs. + private static Dictionary ToDictionary( + string contentTsv, + bool skipChecks = false) { + var entries = new Dictionary(); + var lineNumber = 0; + foreach (var line in contentTsv.Split(LineSeparators, StringSplitOptions.None)) { + lineNumber += 1; + var lineTrimmed = line.Trim(); + if (lineTrimmed.Length == 0) { + continue; + } + + var termSeparatorPos = lineTrimmed.IndexOf(TermSeparator); + if (termSeparatorPos == -1) { + throw new ArgumentException($"Entry on line {lineNumber} does not contain term separator: {line}"); + } + + var source = lineTrimmed.Substring(0, termSeparatorPos).Trim(); + var target = lineTrimmed.Substring(termSeparatorPos + 1).Trim(); + if (!skipChecks) { + if (target.Contains(TermSeparator)) { + throw new ArgumentException($"Entry on line {lineNumber} contains more than one term separator: {line}"); + } + + ValidateGlossaryTerm(source); + ValidateGlossaryTerm(target); + } + + try { + entries.Add(source, target); + } catch (ArgumentException exception) { + throw new ArgumentException($"Entry on line {lineNumber} duplicates source term \"{source}\"", exception); + } + } + + if (!skipChecks && entries.Count == 0) { + throw new ArgumentException($"Argument {nameof(contentTsv)} contains no TSV entries"); + } + + return entries; + } +} diff --git a/src/DeepL/Model/GlossaryInfo.cs b/src/DeepL/Model/GlossaryInfo.cs index c82b13a..cb55636 100644 --- a/src/DeepL/Model/GlossaryInfo.cs +++ b/src/DeepL/Model/GlossaryInfo.cs @@ -5,64 +5,64 @@ using System; using System.Text.Json.Serialization; -namespace DeepL.Model { - /// Information about a glossary, excluding the entry list. - public sealed class GlossaryInfo { - /// Initializes a new containing information about a glossary. - /// ID of the associated glossary. - /// Name of the glossary chosen during creation. - /// true if the glossary may be used for translations, otherwise false. - /// Language code of the source terms in the glossary. - /// Language code of the target terms in the glossary. - /// when the glossary was created. - /// The number of source-target entry pairs in the glossary. - /// - /// The constructor for this class (and all other Model classes) should not be used by library users. Ideally it - /// would be marked , but needs to be for JSON deserialization. - /// In future this function may have backwards-incompatible changes. - /// - [JsonConstructor] - public GlossaryInfo( - string glossaryId, - string name, - bool ready, - string sourceLanguageCode, - string targetLanguageCode, - DateTime creationTime, - int entryCount) { - (GlossaryId, Name, Ready, SourceLanguageCode, TargetLanguageCode, CreationTime, EntryCount) = - (glossaryId, name, ready, sourceLanguageCode, targetLanguageCode, creationTime, entryCount); - } +namespace DeepL.Model; - /// ID of the associated glossary. - public string GlossaryId { get; } +/// Information about a glossary, excluding the entry list. +public sealed class GlossaryInfo { + /// Initializes a new containing information about a glossary. + /// ID of the associated glossary. + /// Name of the glossary chosen during creation. + /// true if the glossary may be used for translations, otherwise false. + /// Language code of the source terms in the glossary. + /// Language code of the target terms in the glossary. + /// when the glossary was created. + /// The number of source-target entry pairs in the glossary. + /// + /// The constructor for this class (and all other Model classes) should not be used by library users. Ideally it + /// would be marked , but needs to be for JSON deserialization. + /// In future this function may have backwards-incompatible changes. + /// + [JsonConstructor] + public GlossaryInfo( + string glossaryId, + string name, + bool ready, + string sourceLanguageCode, + string targetLanguageCode, + DateTime creationTime, + int entryCount) { + (GlossaryId, Name, Ready, SourceLanguageCode, TargetLanguageCode, CreationTime, EntryCount) = + (glossaryId, name, ready, sourceLanguageCode, targetLanguageCode, creationTime, entryCount); + } - /// Name of the glossary chosen during creation. - public string Name { get; } + /// ID of the associated glossary. + public string GlossaryId { get; } - /// true if the glossary may be used for translations, otherwise false. - public bool Ready { get; } + /// Name of the glossary chosen during creation. + public string Name { get; } - /// Language code of the source terms in the glossary. - [JsonPropertyName("source_lang")] - public string SourceLanguageCode { get; } + /// true if the glossary may be used for translations, otherwise false. + public bool Ready { get; } - /// Language code of the target terms in the glossary. - [JsonPropertyName("target_lang")] - public string TargetLanguageCode { get; } + /// Language code of the source terms in the glossary. + [JsonPropertyName("source_lang")] + public string SourceLanguageCode { get; } - /// when the glossary was created. - public DateTime CreationTime { get; } + /// Language code of the target terms in the glossary. + [JsonPropertyName("target_lang")] + public string TargetLanguageCode { get; } - /// The number of source-target entry pairs in the glossary. - public int EntryCount { get; } + /// when the glossary was created. + public DateTime CreationTime { get; } - /// Creates a string containing the name and ID of the current object. - /// A string containing the name and ID of the object. - /// - /// This function is for diagnostic purposes only; the content of the returned string is exempt from backwards - /// compatibility. - /// - public override string ToString() => $"Glossary \"{Name}\" ({GlossaryId})"; - } + /// The number of source-target entry pairs in the glossary. + public int EntryCount { get; } + + /// Creates a string containing the name and ID of the current object. + /// A string containing the name and ID of the object. + /// + /// This function is for diagnostic purposes only; the content of the returned string is exempt from backwards + /// compatibility. + /// + public override string ToString() => $"Glossary \"{Name}\" ({GlossaryId})"; } diff --git a/src/DeepL/Model/GlossaryLanguagePair.cs b/src/DeepL/Model/GlossaryLanguagePair.cs index a54dde5..2b712fc 100644 --- a/src/DeepL/Model/GlossaryLanguagePair.cs +++ b/src/DeepL/Model/GlossaryLanguagePair.cs @@ -4,31 +4,31 @@ using System.Text.Json.Serialization; -namespace DeepL.Model { - /// Information about a language pair supported for glossaries. - public sealed class GlossaryLanguagePair { - /// - /// Initializes a new containing information about a language pair supported - /// for glossaries. - /// - /// Language code of the source terms in the glossary. - /// Language code of the target terms in the glossary. - /// - /// The constructor for this class (and all other Model classes) should not be used by library users. Ideally it - /// would be marked , but needs to be for JSON deserialization. - /// In future this function may have backwards-incompatible changes. - /// - [JsonConstructor] - public GlossaryLanguagePair(string sourceLanguageCode, string targetLanguageCode) { - (SourceLanguageCode, TargetLanguageCode) = (sourceLanguageCode, targetLanguageCode); - } +namespace DeepL.Model; - /// Language code of the source terms in the glossary. - [JsonPropertyName("source_lang")] - public string SourceLanguageCode { get; } - - /// Language code of the target terms in the glossary. - [JsonPropertyName("target_lang")] - public string TargetLanguageCode { get; } +/// Information about a language pair supported for glossaries. +public sealed class GlossaryLanguagePair { + /// + /// Initializes a new containing information about a language pair supported + /// for glossaries. + /// + /// Language code of the source terms in the glossary. + /// Language code of the target terms in the glossary. + /// + /// The constructor for this class (and all other Model classes) should not be used by library users. Ideally it + /// would be marked , but needs to be for JSON deserialization. + /// In future this function may have backwards-incompatible changes. + /// + [JsonConstructor] + public GlossaryLanguagePair(string sourceLanguageCode, string targetLanguageCode) { + (SourceLanguageCode, TargetLanguageCode) = (sourceLanguageCode, targetLanguageCode); } + + /// Language code of the source terms in the glossary. + [JsonPropertyName("source_lang")] + public string SourceLanguageCode { get; } + + /// Language code of the target terms in the glossary. + [JsonPropertyName("target_lang")] + public string TargetLanguageCode { get; } } diff --git a/src/DeepL/Model/Interfaces/ITranslator.cs b/src/DeepL/Model/Interfaces/ITranslator.cs new file mode 100644 index 0000000..865cea0 --- /dev/null +++ b/src/DeepL/Model/Interfaces/ITranslator.cs @@ -0,0 +1,401 @@ +// Copyright 2022 DeepL SE (https://www.deepl.com) +// Use of this source code is governed by an MIT +// license that can be found in the LICENSE file. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using DeepL.Model; +using DeepL.Model.Exceptions; +using DeepL.Model.Options; + +namespace DeepL.Model.Interfaces { + public interface ITranslator : IDisposable { + + /// Retrieves the usage in the current billing period for this DeepL account. + /// The cancellation token to cancel operation. + /// object containing account usage information. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task GetUsageAsync(CancellationToken cancellationToken = default); + + /// Translate specified texts from source language into target language. + /// Texts to translate; must not be empty. + /// Language code of the input language, or null to use auto-detection. + /// Language code of the desired output language. + /// Extra influencing translation. + /// The cancellation token to cancel operation. + /// Texts translated into specified target language. + /// If any argument is invalid. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task TranslateTextAsync( + IEnumerable texts, + string? sourceLanguageCode, + string targetLanguageCode, + TextTranslateOptions? options = null, + CancellationToken cancellationToken = default); + + /// Translate specified text from source language into target language. + /// Text to translate; must not be empty. + /// Language code of the input language, or null to use auto-detection. + /// Language code of the desired output language. + /// influencing translation. + /// The cancellation token to cancel operation. + /// Text translated into specified target language. + /// If any argument is invalid. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task TranslateTextAsync( + string text, + string? sourceLanguageCode, + string targetLanguageCode, + TextTranslateOptions? options = null, + CancellationToken cancellationToken = default); + + /// + /// Translate document at specified input from source language to target language and + /// store the translated document at specified output . + /// + /// object containing path to input document. + /// object containing path to store translated document. + /// Language code of the input language, or null to use auto-detection. + /// Language code of the desired output language. + /// influencing translation. + /// The cancellation token to cancel operation. + /// If the output path is occupied. + /// + /// If cancellation was requested, or an error occurs while translating the document. If the document was uploaded + /// successfully, then the contains the document ID and + /// key that may be used to retrieve the document. + /// If cancellation was requested, the will be a + /// . + /// + Task TranslateDocumentAsync( + FileInfo inputFileInfo, + FileInfo outputFileInfo, + string? sourceLanguageCode, + string targetLanguageCode, + DocumentTranslateOptions? options = null, + CancellationToken cancellationToken = default); + + /// + /// Translate specified document content from source language to target language and store the translated document + /// content to specified stream. + /// + /// containing input document content. + /// Name of the input file. The file extension is used to determine file type. + /// to store translated document content. + /// Language code of the input language, or null to use auto-detection. + /// Language code of the desired output language. + /// influencing translation. + /// The cancellation token to cancel operation. + /// + /// If cancellation was requested, or an error occurs while translating the document. If the document was uploaded + /// successfully, then the contains the document ID and + /// key that may be used to retrieve the document. + /// If cancellation was requested, the will be a + /// . + /// + Task TranslateDocumentAsync( + Stream inputFile, + string inputFileName, + Stream outputFile, + string? sourceLanguageCode, + string targetLanguageCode, + DocumentTranslateOptions? options = null, + CancellationToken cancellationToken = default); + + /// + /// Upload document at specified input for translation from source language to target + /// language. + /// + /// object containing path to input document. + /// Language code of the input language, or null to use auto-detection. + /// Language code of the desired output language. + /// influencing translation. + /// The cancellation token to cancel operation. + /// object associated with the in-progress document translation. + /// If any argument is invalid. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task TranslateDocumentUploadAsync( + FileInfo inputFileInfo, + string? sourceLanguageCode, + string targetLanguageCode, + DocumentTranslateOptions? options = null, + CancellationToken cancellationToken = default); + + /// Upload document content with specified filename for translation from source language to target language. + /// containing input document content. + /// Name of the input file. The file extension is used to determine file type. + /// Language code of the input language, or null to use auto-detection. + /// Language code of the desired output language. + /// influencing translation. + /// The cancellation token to cancel operation. + /// object associated with the in-progress document translation. + /// If any argument is invalid. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task TranslateDocumentUploadAsync( + Stream inputFile, + string inputFileName, + string? sourceLanguageCode, + string targetLanguageCode, + DocumentTranslateOptions? options = null, + CancellationToken cancellationToken = default); + + /// + /// Retrieve the status of in-progress document translation associated with specified + /// . + /// + /// associated with an in-progress document translation. + /// The cancellation token to cancel operation. + /// object containing the document translation status. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task TranslateDocumentStatusAsync( + DocumentHandle handle, + CancellationToken cancellationToken = default); + + /// + /// Checks document translation status and asynchronously-waits until document translation is complete or fails + /// due to an error. + /// + /// associated with an in-progress document translation. + /// The cancellation token to cancel operation. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task TranslateDocumentWaitUntilDoneAsync( + DocumentHandle handle, + CancellationToken cancellationToken = default); + + /// + /// Downloads the resulting translated document associated with specified to the + /// specified . The for the document must have + /// == true. + /// + /// associated with an in-progress document translation. + /// object containing path to store translated document. + /// The cancellation token to cancel operation. + /// If the output path is occupied. + /// + /// If the document is not ready to be downloaded, that is, the document status + /// is not . + /// + /// + /// If any other error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task TranslateDocumentDownloadAsync( + DocumentHandle handle, + FileInfo outputFileInfo, + CancellationToken cancellationToken = default); + + /// + /// Downloads the resulting translated document associated with specified to the + /// specified . The for the document must have + /// == true. + /// + /// associated with an in-progress document translation. + /// to store translated document content. + /// The cancellation token to cancel operation. + /// + /// If the document is not ready to be downloaded, that is, the document status + /// is not . + /// + /// + /// If any other error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task TranslateDocumentDownloadAsync( + DocumentHandle handle, + Stream outputFile, + CancellationToken cancellationToken = default); + + /// Retrieves the list of supported translation source languages. + /// The cancellation token to cancel operation. + /// Array of objects representing the available translation source languages. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task GetSourceLanguagesAsync(CancellationToken cancellationToken = default); + + /// Retrieves the list of supported translation target languages. + /// The cancellation token to cancel operation. + /// Array of objects representing the available translation target languages. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task GetTargetLanguagesAsync(CancellationToken cancellationToken = default); + + /// + /// Retrieves the list of supported glossary language pairs. When creating glossaries, the source and target + /// language pair must match one of the available language pairs. + /// + /// The cancellation token to cancel operation. + /// Array of objects representing the available glossary language pairs. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task GetGlossaryLanguagesAsync(CancellationToken cancellationToken = default); + + /// + /// Creates a glossary in your DeepL account with the specified details and returns a + /// object with details about the newly created glossary. The glossary can be used in translations to override + /// translations for specific terms (words). The glossary source and target languages must match the languages of + /// translations for which it will be used. + /// + /// User-defined name to assign to the glossary; must not be empty. + /// Language code of the source terms language. + /// Language code of the target terms language. + /// Glossary entry list. + /// The cancellation token to cancel operation. + /// object with details about the newly created glossary. + /// If any argument is invalid. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task CreateGlossaryAsync( + string name, + string sourceLanguageCode, + string targetLanguageCode, + GlossaryEntries entries, + CancellationToken cancellationToken = default); + + /// + /// Creates a glossary in your DeepL account with the specified details and returns a + /// object with details about the newly created glossary. The glossary can be used in translations to override + /// translations for specific terms (words). The glossary source and target languages must match the languages of + /// translations for which it will be used. + /// + /// User-defined name to assign to the glossary; must not be empty. + /// Language code of the source terms language. + /// Language code of the target terms language. + /// containing CSV content. + /// The cancellation token to cancel operation. + /// object with details about the newly created glossary. + /// If any argument is invalid. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task CreateGlossaryFromCsvAsync( + string name, + string sourceLanguageCode, + string targetLanguageCode, + Stream csvFile, + CancellationToken cancellationToken = default); + + /// + /// Retrieves information about the glossary with the specified ID and returns a + /// object containing details. This does not retrieve the glossary entries; to retrieve entries use + /// + /// + /// ID of glossary to retrieve. + /// The cancellation token to cancel operation. + /// object with details about the specified glossary. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task GetGlossaryAsync( + string glossaryId, + CancellationToken cancellationToken = default); + + /// Asynchronously waits until the given glossary is ready to be used for translations. + /// ID of glossary to ensure ready. + /// The cancellation token to cancel operation. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task WaitUntilGlossaryReadyAsync( + string glossaryId, + CancellationToken cancellationToken = default); + + /// + /// Retrieves information about all glossaries and returns an array of objects + /// containing details. This does not retrieve the glossary entries; to retrieve entries use + /// + /// + /// The cancellation token to cancel operation. + /// Array of objects with details about each glossary. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task ListGlossariesAsync(CancellationToken cancellationToken = default); + + /// + /// Retrieves the entries containing within the glossary with the specified ID and returns them as a + /// . + /// + /// ID of glossary for which to retrieve entries. + /// The cancellation token to cancel operation. + /// containing entry pairs of the glossary. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task GetGlossaryEntriesAsync( + string glossaryId, + CancellationToken cancellationToken = default); + + /// + /// Retrieves the entries containing within the glossary and returns them as a . + /// + /// object corresponding to glossary for which to retrieve entries. + /// The cancellation token to cancel operation. + /// containing entry pairs of the glossary. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task GetGlossaryEntriesAsync( + GlossaryInfo glossary, + CancellationToken cancellationToken = default); + + /// Deletes the glossary with the specified ID. + /// ID of glossary to delete. + /// The cancellation token to cancel operation. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task DeleteGlossaryAsync( + string glossaryId, + CancellationToken cancellationToken = default); + + /// Deletes the specified glossary. + /// object corresponding to glossary to delete. + /// The cancellation token to cancel operation. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + Task DeleteGlossaryAsync( + GlossaryInfo glossary, + CancellationToken cancellationToken = default); + } +} diff --git a/src/DeepL/Model/Language.cs b/src/DeepL/Model/Language.cs index f7ae7cc..c79ae3b 100644 --- a/src/DeepL/Model/Language.cs +++ b/src/DeepL/Model/Language.cs @@ -6,152 +6,153 @@ using System.Globalization; using System.Text.Json.Serialization; -namespace DeepL.Model { +namespace DeepL.Model; + + +/// +/// A language supported by DeepL translation. The class provides functions to retrieve +/// the available source and target languages. objects are considered equal if their +/// language codes match. +/// +/// +/// +public class Language : IEquatable { + /// Initializes a new Language object. + /// The language code. + /// The name of the language in English. + protected Language(string code, string name) { + Code = LanguageCode.Standardize(code); + Name = name; + } + + /// The name of the language in English, for example "Italian" or "Romanian". + public string Name { get; } + /// - /// A language supported by DeepL translation. The class provides functions to retrieve - /// the available source and target languages. objects are considered equal if their - /// language codes match. + /// The language code, for example "it", "ro" or "en-US". Language codes follow ISO 639-1 with an optional + /// regional code from ISO 3166-1. /// - /// - /// - public class Language : IEquatable { - /// Initializes a new Language object. - /// The language code. - /// The name of the language in English. - protected Language(string code, string name) { - Code = LanguageCode.Standardize(code); - Name = name; - } + [JsonPropertyName("language")] + public string Code { get; } + + /// Creates a object corresponding to this language. + public CultureInfo CultureInfo => new CultureInfo(Code); - /// The name of the language in English, for example "Italian" or "Romanian". - public string Name { get; } - - /// - /// The language code, for example "it", "ro" or "en-US". Language codes follow ISO 639-1 with an optional - /// regional code from ISO 3166-1. - /// - [JsonPropertyName("language")] - public string Code { get; } - - /// Creates a object corresponding to this language. - public CultureInfo CultureInfo => new CultureInfo(Code); - - /// - /// Returns true if the other object has the same language code, otherwise - /// false. - /// - /// to compare with. - /// true if languages have the same language code, otherwise false. - public bool Equals(Language? other) { - if (ReferenceEquals(null, other)) { - return false; - } - - if (ReferenceEquals(this, other)) { - return true; - } - - return Code == other.Code; + /// + /// Returns true if the other object has the same language code, otherwise + /// false. + /// + /// to compare with. + /// true if languages have the same language code, otherwise false. + public bool Equals(Language? other) { + if (ReferenceEquals(null, other)) { + return false; } - /// Converts the language to a string containing the name and language code. - /// A string containing the name and language code of the language. - /// - /// This function is for diagnostic purposes only; the content of the returned string is exempt from backwards - /// compatibility. - /// - public override string ToString() => $"{Name} ({Code})"; - - /// Implicitly cast to string using the language code. - /// object to cast into string. - /// String containing the language code. - public static implicit operator string(Language language) => language.Code; - - /// - /// Determines whether this instance and a specified object, which must also be a object, - /// have the same value. objects are considered equal if their language codes match. - /// - /// The Language to compare to this instance. - /// - /// true if is a and its value is the same as this - /// instance; otherwise, false. If is null, the method - /// returns false. - /// - public override bool Equals(object? obj) => ReferenceEquals(this, obj) || (obj is Language other && Equals(other)); - - /// - /// Returns the hash code for this object, that is simply the hash code of the language - /// code. - /// - /// A 32-bit signed integer hash code. - public override int GetHashCode() => Code.GetHashCode(); - - /// Determines whether two specified Languages have the same value. - /// The first Language to compare, or null. - /// The second Language to compare, or null. - /// - /// true if the value of is the same as the value of ; - /// otherwise, false. - /// - public static bool operator ==(Language? a, Language? b) { - if ((object?)a == null || (object?)b == null) { - return Equals(a, b); - } - - return a.Equals(b); + if (ReferenceEquals(this, other)) { + return true; } - /// Determines whether two specified Languages have different values. - /// The first Language to compare, or null. - /// The second Language to compare, or null. - /// - /// true if the value of is different from the value of , - /// otherwise false. - /// - public static bool operator !=(Language? a, Language? b) => !(a == b); + return Code == other.Code; } + /// Converts the language to a string containing the name and language code. + /// A string containing the name and language code of the language. + /// + /// This function is for diagnostic purposes only; the content of the returned string is exempt from backwards + /// compatibility. + /// + public override string ToString() => $"{Name} ({Code})"; + + /// Implicitly cast to string using the language code. + /// object to cast into string. + /// String containing the language code. + public static implicit operator string(Language language) => language.Code; + /// - /// A source language supported by DeepL translation, returned by . + /// Determines whether this instance and a specified object, which must also be a object, + /// have the same value. objects are considered equal if their language codes match. /// - public sealed class SourceLanguage : Language { - /// Initializes a new Language object. - /// The language code. - /// The name of the language in English. - /// - /// The constructor for this class (and all other Model classes) should not be used by library users. Ideally it - /// would be marked , but needs to be for JSON deserialization. - /// In future this function may have backwards-incompatible changes. - /// - [JsonConstructor] - public SourceLanguage(string code, string name) : base(code, name) { - } - } + /// The Language to compare to this instance. + /// + /// true if is a and its value is the same as this + /// instance; otherwise, false. If is null, the method + /// returns false. + /// + public override bool Equals(object? obj) => ReferenceEquals(this, obj) || (obj is Language other && Equals(other)); /// - /// A target language supported by DeepL translation, returned by . + /// Returns the hash code for this object, that is simply the hash code of the language + /// code. /// - public sealed class TargetLanguage : Language { - /// Initializes a new Language object. - /// The language code. - /// The name of the language in English. - /// - /// true if this language supports the parameter for - /// translations, otherwise false. - /// - /// - /// The constructor for this class (and all other Model classes) should not be used by library users. Ideally it - /// would be marked , but needs to be for JSON deserialization. - /// In future this function may have backwards-incompatible changes. - /// - [JsonConstructor] - public TargetLanguage(string code, string name, bool supportsFormality) : base(code, name) { - SupportsFormality = supportsFormality; + /// A 32-bit signed integer hash code. + public override int GetHashCode() => Code.GetHashCode(); + + /// Determines whether two specified Languages have the same value. + /// The first Language to compare, or null. + /// The second Language to compare, or null. + /// + /// true if the value of is the same as the value of ; + /// otherwise, false. + /// + public static bool operator ==(Language? a, Language? b) { + if ((object?)a == null || (object?)b == null) { + return Equals(a, b); } - /// - /// true if the language supports the parameter for translations, otherwise false. - /// - public bool SupportsFormality { get; } + return a.Equals(b); } + + /// Determines whether two specified Languages have different values. + /// The first Language to compare, or null. + /// The second Language to compare, or null. + /// + /// true if the value of is different from the value of , + /// otherwise false. + /// + public static bool operator !=(Language? a, Language? b) => !(a == b); +} + +/// +/// A source language supported by DeepL translation, returned by . +/// +public sealed class SourceLanguage : Language { + /// Initializes a new Language object. + /// The language code. + /// The name of the language in English. + /// + /// The constructor for this class (and all other Model classes) should not be used by library users. Ideally it + /// would be marked , but needs to be for JSON deserialization. + /// In future this function may have backwards-incompatible changes. + /// + [JsonConstructor] + public SourceLanguage(string code, string name) : base(code, name) { + } +} + +/// +/// A target language supported by DeepL translation, returned by . +/// +public sealed class TargetLanguage : Language { + /// Initializes a new Language object. + /// The language code. + /// The name of the language in English. + /// + /// true if this language supports the parameter for + /// translations, otherwise false. + /// + /// + /// The constructor for this class (and all other Model classes) should not be used by library users. Ideally it + /// would be marked , but needs to be for JSON deserialization. + /// In future this function may have backwards-incompatible changes. + /// + [JsonConstructor] + public TargetLanguage(string code, string name, bool supportsFormality) : base(code, name) { + SupportsFormality = supportsFormality; + } + + /// + /// true if the language supports the parameter for translations, otherwise false. + /// + public bool SupportsFormality { get; } } diff --git a/src/DeepL/Model/LanguageCode.cs b/src/DeepL/Model/LanguageCode.cs new file mode 100644 index 0000000..15f9ce9 --- /dev/null +++ b/src/DeepL/Model/LanguageCode.cs @@ -0,0 +1,131 @@ +// Copyright 2022 DeepL SE (https://www.deepl.com) +// Use of this source code is governed by an MIT +// license that can be found in the LICENSE file. + +using DeepL.Model.Interfaces; + +namespace DeepL.Model; + +/// +/// Language codes for the languages currently supported by DeepL translation. New languages may be added in +/// future; to retrieve the currently supported languages use and +/// . +/// +public static class LanguageCode { + /// Bulgarian language code, may be used as source or target language. + public const string Bulgarian = "bg"; + + /// Czech language code, may be used as source or target language. + public const string Czech = "cs"; + + /// Danish language code, may be used as source or target language. + public const string Danish = "da"; + + /// German language code, may be used as source or target language. + public const string German = "de"; + + /// Greek language code, may be used as source or target language. + public const string Greek = "el"; + + /// English language code, may only be used as a source language. + public const string English = "en"; + + /// British English language code, may only be used as a target language. + public const string EnglishBritish = "en-GB"; + + /// American English language code, may only be used as a target language. + public const string EnglishAmerican = "en-US"; + + /// Spanish language code, may be used as source or target language. + public const string Spanish = "es"; + + /// Estonian language code, may be used as source or target language. + public const string Estonian = "et"; + + /// Finnish language code, may be used as source or target language. + public const string Finnish = "fi"; + + /// French language code, may be used as source or target language. + public const string French = "fr"; + + /// Hungarian language code, may be used as source or target language. + public const string Hungarian = "hu"; + + /// Indonesian language code, may be used as source or target language. + public const string Indonesian = "id"; + + /// Italian language code, may be used as source or target language. + public const string Italian = "it"; + + /// Japanese language code, may be used as source or target language. + public const string Japanese = "ja"; + + /// Korean language code, may be used as source or target language. + public const string Korean = "ko"; + + /// Lithuanian language code, may be used as source or target language. + public const string Lithuanian = "lt"; + + /// Latvian language code, may be used as source or target language. + public const string Latvian = "lv"; + + /// Norwegian (bokmål) language code, may be used as source or target language. + public const string Norwegian = "nb"; + + /// Dutch language code, may be used as source or target language. + public const string Dutch = "nl"; + + /// Polish language code, may be used as source or target language. + public const string Polish = "pl"; + + /// Portuguese language code, may only be used as a source language. + public const string Portuguese = "pt"; + + /// Brazilian Portuguese language code, may only be used as a target language. + public const string PortugueseBrazilian = "pt-BR"; + + /// European Portuguese language code, may only be used as a target language. + public const string PortugueseEuropean = "pt-PT"; + + /// Romanian language code, may be used as source or target language. + public const string Romanian = "ro"; + + /// Russian language code, may be used as source or target language. + public const string Russian = "ru"; + + /// Slovak language code, may be used as source or target language. + public const string Slovak = "sk"; + + /// Slovenian language code, may be used as source or target language. + public const string Slovenian = "sl"; + + /// Swedish language code, may be used as source or target language. + public const string Swedish = "sv"; + + /// Turkish language code, may be used as source or target language. + public const string Turkish = "tr"; + + /// Ukrainian language code, may be used as source or target language. + public const string Ukrainian = "uk"; + + /// Chinese language code, may be used as source or target language. + public const string Chinese = "zh"; + + /// Removes the regional variant (if any) from the given language code + /// Language code possibly containing a regional variant. + /// The language code without a regional variant. + public static string RemoveRegionalVariant(string code) => code.Split(new[] { '-' }, 2)[0].ToLowerInvariant(); + + /// + /// Changes the upper- and lower-casing of the given language code to match ISO 639-1 with an optional regional + /// code from ISO 3166-1. + /// + /// String containing language code to standardize. + /// String containing the standardized language code. + internal static string Standardize(string code) { + var parts = code.Split(new[] { '-' }, 2); + return parts.Length == 1 + ? parts[0].ToLowerInvariant() + : $"{parts[0].ToLowerInvariant()}-{parts[1].ToUpperInvariant()}"; + } +} diff --git a/src/DeepL/Model/Options/DocumentTranslateOptions.cs b/src/DeepL/Model/Options/DocumentTranslateOptions.cs new file mode 100644 index 0000000..b3035d0 --- /dev/null +++ b/src/DeepL/Model/Options/DocumentTranslateOptions.cs @@ -0,0 +1,28 @@ +// Copyright 2022 DeepL SE (https://www.deepl.com) +// Use of this source code is governed by an MIT +// license that can be found in the LICENSE file. + +namespace DeepL.Model.Options; + +/// +/// Options to control document translation behaviour. These options may be provided to +/// document translate functions. +/// +public sealed class DocumentTranslateOptions { + /// Initializes a new object. + public DocumentTranslateOptions() { } + + /// Initializes a new object including the given glossary. + /// Glossary to use in translation. + public DocumentTranslateOptions(GlossaryInfo glossary) : this() { + GlossaryId = glossary.GlossaryId; + } + + /// Controls whether translations should lean toward formal or informal language. + /// This option is only applicable for target languages that support the formality option. + /// + public Formality Formality { get; set; } = Formality.Default; + + /// Specifies the ID of a glossary to use with the translation. + public string? GlossaryId { get; set; } +} diff --git a/src/DeepL/Model/Options/TextTranslateOptions.cs b/src/DeepL/Model/Options/TextTranslateOptions.cs new file mode 100644 index 0000000..25e4916 --- /dev/null +++ b/src/DeepL/Model/Options/TextTranslateOptions.cs @@ -0,0 +1,56 @@ +// Copyright 2022 DeepL SE (https://www.deepl.com) +// Use of this source code is governed by an MIT +// license that can be found in the LICENSE file. + +using System.Collections.Generic; +using DeepL.Model; + +namespace DeepL.Model.Options; + +/// +/// Options to control text translation behaviour. These options may be provided to text +/// translate functions. +/// +public sealed class TextTranslateOptions { + /// Initializes a new object. + public TextTranslateOptions() { } + + /// Initializes a new object including the given glossary. + /// Glossary to use in translation. + public TextTranslateOptions(GlossaryInfo glossary) : this() { + GlossaryId = glossary.GlossaryId; + } + + /// Controls whether translations should lean toward formal or informal language. + /// This option is only applicable for target languages that support the formality option. + /// + public Formality Formality { get; set; } = Formality.Default; + + /// Specifies the ID of a glossary to use with the translation. + public string? GlossaryId { get; set; } + + /// Specifies a list of XML tags containing content that should not be translated. + public List IgnoreTags { get; } = new List(); + + /// Specifies a list of XML tags that should not be used to split text into sentences. + public List NonSplittingTags { get; } = new List(); + + /// Set to false to disable automatic tag detection, default is true. + public bool OutlineDetection { get; set; } = true; + + /// + /// Set to true to prevent the translation engine from correcting some formatting aspects, and instead leave the + /// formatting unchanged, default is false. + /// + public bool PreserveFormatting { get; set; } + + /// Specifies how input translation text should be split into sentences. + /// + public SentenceSplittingMode SentenceSplittingMode { get; set; } = SentenceSplittingMode.All; + + /// Specifies a list of XML tags that should be used to split text into sentences. + public List SplittingTags { get; } = new List(); + + /// Type of tags to parse before translation, only "xml" and "html" are currently available. + public string? TagHandling { get; set; } +} diff --git a/src/DeepL/Model/Options/TranslatorOptions.cs b/src/DeepL/Model/Options/TranslatorOptions.cs new file mode 100644 index 0000000..e8fe617 --- /dev/null +++ b/src/DeepL/Model/Options/TranslatorOptions.cs @@ -0,0 +1,96 @@ +// Copyright 2022 DeepL SE (https://www.deepl.com) +// Use of this source code is governed by an MIT +// license that can be found in the LICENSE file. + +using System; +using System.Collections.Generic; +using System.Net.Http; + +namespace DeepL.Model.Options; + + +/// Class containing containing options controlling behaviour. +public sealed class TranslatorOptions { + + /// + /// The DeepL API Auth Key + /// + public string AuthKey { get; set; } = string.Empty; + + /// + /// HTTP headers attached to every HTTP request. By default no extra headers are used. Note that during + /// initialization headers for Authorization and User-Agent are added, unless they are + /// overridden in this option. + /// + public Dictionary Headers { get; set; } = new Dictionary(); + + /// + /// The maximum number of failed attempts that will retry, per request. By default 5 retries + /// are made. Note: only errors due to transient conditions are retried. Only used if is + /// unset. + /// + public int MaximumNetworkRetries { get; set; } = 5; + + /// + /// Connection timeout for HTTP requests, including all retries, the default is 100 seconds. Only used if + /// is unset. + /// + public TimeSpan OverallConnectionTimeout { get; set; } = TimeSpan.FromSeconds(100); + + /// + /// Connection timeout used for each HTTP request retry, the default is 10 seconds. Only used if + /// is unset. + /// + public TimeSpan PerRetryConnectionTimeout { get; set; } = TimeSpan.FromSeconds(10); + + /// + /// The base URL for DeepL's API that may be overridden for testing purposes. By default the correct DeepL API URL + /// is selected based on the user account type (free or paid). + /// + public string? ServerUrl { get; set; } + + /// + /// Factory function returning an to be used by for HTTP requests, + /// and a flag whether to call in . + /// Override this function to provide your own . Note that overriding this function will disable + /// the built-in retrying of failed-requests, so you must provide your own retry policy. + /// + public Func? ClientFactory { get; set; } + + public bool sendPlatformInfo { get; set; } = true; + + public AppInfo? appInfo { get; set; } +} + +/// +/// Struct containing an to be used by and flag whether it +/// should be disposed in . +/// +public struct HttpClientAndDisposeFlag { + /// + /// used by for all requests to DeepL API. + /// + public HttpClient HttpClient { get; set; } + + /// + /// true if the provided should be disposed of by ; + /// false if you + /// intend to reuse it. + /// + public bool DisposeClient { get; set; } +} + +/// +/// Represents the identifying information of an application that uses this library, its name and version. +/// Example: AppName = "myDotNetCustomerChatApp", AppVersion = "1.2.3" +/// +public struct AppInfo { + /// + /// Name of the application using this client library + /// + public string AppName { get; set; } + /// + /// Version of the application using this client library + /// + public string AppVersion { get; set; } +} diff --git a/src/DeepL/Model/SentenceSplittingMode.cs b/src/DeepL/Model/SentenceSplittingMode.cs new file mode 100644 index 0000000..24ac401 --- /dev/null +++ b/src/DeepL/Model/SentenceSplittingMode.cs @@ -0,0 +1,24 @@ +// Copyright 2022 DeepL SE (https://www.deepl.com) +// Use of this source code is governed by an MIT +// license that can be found in the LICENSE file. + +namespace DeepL.Model; + + +/// Enum controlling how input translation text should be split into sentences. +public enum SentenceSplittingMode { + /// + /// Input translation text will be split into sentences using both newlines and punctuation, this is the default + /// behaviour. + /// + All, + + /// + /// Input text will not be split into sentences. This is advisable for applications where each input translation + /// text is only one sentence. + /// + Off, + + /// Input translation text will be split into sentences using only punctuation but not newlines. + NoNewlines +} diff --git a/src/DeepL/Model/TextResult.cs b/src/DeepL/Model/TextResult.cs index 8c14069..48abd7e 100644 --- a/src/DeepL/Model/TextResult.cs +++ b/src/DeepL/Model/TextResult.cs @@ -4,32 +4,33 @@ using System.Text.Json.Serialization; -namespace DeepL.Model { - /// The result of a text translation. - public sealed class TextResult { - /// Initializes a new instance of . - /// Translated text. - /// The detected language code of the input text. - /// - /// The constructor for this class (and all other Model classes) should not be used by library users. Ideally it - /// would be marked , but needs to be for JSON deserialization. - /// In future this function may have backwards-incompatible changes. - /// - [JsonConstructor] - public TextResult(string text, string detectedSourceLanguageCode) { - Text = text; - DetectedSourceLanguageCode = LanguageCode.Standardize(detectedSourceLanguageCode); - } +namespace DeepL.Model; - /// The translated text. - public string Text { get; } - /// The language code of the source text detected by DeepL. - [JsonPropertyName("detected_source_language")] - public string DetectedSourceLanguageCode { get; } - - /// Returns the translated text. - /// The translated text. - public override string ToString() => Text; +/// The result of a text translation. +public sealed class TextResult { + /// Initializes a new instance of . + /// Translated text. + /// The detected language code of the input text. + /// + /// The constructor for this class (and all other Model classes) should not be used by library users. Ideally it + /// would be marked , but needs to be for JSON deserialization. + /// In future this function may have backwards-incompatible changes. + /// + [JsonConstructor] + public TextResult(string text, string detectedSourceLanguageCode) { + Text = text; + DetectedSourceLanguageCode = LanguageCode.Standardize(detectedSourceLanguageCode); } + + /// The translated text. + public string Text { get; } + + /// The language code of the source text detected by DeepL. + [JsonPropertyName("detected_source_language")] + public string DetectedSourceLanguageCode { get; } + + /// Returns the translated text. + /// The translated text. + public override string ToString() => Text; } diff --git a/src/DeepL/Model/Usage.cs b/src/DeepL/Model/Usage.cs index 7ffdaa9..41bf023 100644 --- a/src/DeepL/Model/Usage.cs +++ b/src/DeepL/Model/Usage.cs @@ -4,107 +4,108 @@ using System.Text.Json.Serialization; -namespace DeepL.Model { - /// - /// Information about DeepL account usage for the current billing period, for example the number of characters - /// translated. - /// +namespace DeepL.Model; + + +/// +/// Information about DeepL account usage for the current billing period, for example the number of characters +/// translated. +/// +/// +/// Depending on the account type, some usage types will be omitted. See the +/// API documentation for more information. +/// +public sealed class Usage { + /// Initializes a new object from the given fields object. + /// object containing fields read from JSON data. + internal Usage(in JsonFieldsStruct fieldsStruct) { + Detail? DetailOrNull(long? count, long? limit) { + return count != null && limit != null ? new Detail((long)count, (long)limit) : null; + } + + Character = DetailOrNull(fieldsStruct.CharacterCount, fieldsStruct.CharacterLimit); + Document = DetailOrNull(fieldsStruct.DocumentCount, fieldsStruct.DocumentLimit); + TeamDocument = DetailOrNull(fieldsStruct.TeamDocumentCount, fieldsStruct.TeamDocumentLimit); + } + + /// The character usage if included for the account type, or null. + public Detail? Character { get; } + + /// The document usage if included for the account type, or null. + public Detail? Document { get; } + + /// The team document usage if included for the account type, or null. + public Detail? TeamDocument { get; } + + /// true if any of the usage types included for the account type have been reached. + public bool AnyLimitReached => (Character?.LimitReached ?? false) || (Document?.LimitReached ?? false) || + (TeamDocument?.LimitReached ?? false); + + /// Returns a string representing the usage. + /// A string containing the usage for this billing period. /// - /// Depending on the account type, some usage types will be omitted. See the - /// API documentation for more information. + /// This function is for diagnostic purposes only; the content of the returned string is exempt from backwards + /// compatibility. /// - public sealed class Usage { - /// Initializes a new object from the given fields object. - /// object containing fields read from JSON data. - internal Usage(in JsonFieldsStruct fieldsStruct) { - Detail? DetailOrNull(long? count, long? limit) { - return count != null && limit != null ? new Detail((long)count, (long)limit) : null; - } - - Character = DetailOrNull(fieldsStruct.CharacterCount, fieldsStruct.CharacterLimit); - Document = DetailOrNull(fieldsStruct.DocumentCount, fieldsStruct.DocumentLimit); - TeamDocument = DetailOrNull(fieldsStruct.TeamDocumentCount, fieldsStruct.TeamDocumentLimit); + public override string ToString() { + static string LabelledDetail(string label, Detail? detail) { + return detail == null ? "" : $"\n{label}: {detail}"; } - /// The character usage if included for the account type, or null. - public Detail? Character { get; } + return "Usage this billing period:" + + LabelledDetail("Characters", Character) + + LabelledDetail("Documents", Document) + + LabelledDetail("Team documents", TeamDocument); + } - /// The document usage if included for the account type, or null. - public Detail? Document { get; } + /// + /// Stores the amount used and maximum amount for one usage type. + /// + public sealed class Detail { + /// Initializes a new object. + /// Amount used of one usage type. + /// Maximum amount allowed for one usage type. + internal Detail(long count, long limit) { + (Count, Limit) = (count, limit); + } - /// The team document usage if included for the account type, or null. - public Detail? TeamDocument { get; } + /// The currently used number of items for this usage type. + public long Count { get; } - /// true if any of the usage types included for the account type have been reached. - public bool AnyLimitReached => (Character?.LimitReached ?? false) || (Document?.LimitReached ?? false) || - (TeamDocument?.LimitReached ?? false); + /// The maximum permitted number of items for this usage type. + public long Limit { get; } - /// Returns a string representing the usage. - /// A string containing the usage for this billing period. + /// true if the amount used meets or exceeds the limit, otherwise false. + public bool LimitReached => Count >= Limit; + + /// The usage detail as a string. + /// A string containing the amount used and the limit. /// /// This function is for diagnostic purposes only; the content of the returned string is exempt from backwards /// compatibility. /// - public override string ToString() { - static string LabelledDetail(string label, Detail? detail) { - return detail == null ? "" : $"\n{label}: {detail}"; - } - - return "Usage this billing period:" + - LabelledDetail("Characters", Character) + - LabelledDetail("Documents", Document) + - LabelledDetail("Team documents", TeamDocument); - } + public override string ToString() => $"{Count} of {Limit}"; + } - /// - /// Stores the amount used and maximum amount for one usage type. - /// - public sealed class Detail { - /// Initializes a new object. - /// Amount used of one usage type. - /// Maximum amount allowed for one usage type. - internal Detail(long count, long limit) { - (Count, Limit) = (count, limit); - } - - /// The currently used number of items for this usage type. - public long Count { get; } - - /// The maximum permitted number of items for this usage type. - public long Limit { get; } - - /// true if the amount used meets or exceeds the limit, otherwise false. - public bool LimitReached => Count >= Limit; - - /// The usage detail as a string. - /// A string containing the amount used and the limit. - /// - /// This function is for diagnostic purposes only; the content of the returned string is exempt from backwards - /// compatibility. - /// - public override string ToString() => $"{Count} of {Limit}"; + /// Internal struct used for JSON deserialization of . + internal readonly struct JsonFieldsStruct { + [JsonConstructor] + public JsonFieldsStruct( + long? characterCount, + long? characterLimit, + long? documentCount, + long? documentLimit, + long? teamDocumentCount, + long? teamDocumentLimit) { + (CharacterCount, CharacterLimit, DocumentCount, DocumentLimit, TeamDocumentCount, TeamDocumentLimit) = + (characterCount, characterLimit, documentCount, documentLimit, teamDocumentCount, teamDocumentLimit); } - /// Internal struct used for JSON deserialization of . - internal readonly struct JsonFieldsStruct { - [JsonConstructor] - public JsonFieldsStruct( - long? characterCount, - long? characterLimit, - long? documentCount, - long? documentLimit, - long? teamDocumentCount, - long? teamDocumentLimit) { - (CharacterCount, CharacterLimit, DocumentCount, DocumentLimit, TeamDocumentCount, TeamDocumentLimit) = - (characterCount, characterLimit, documentCount, documentLimit, teamDocumentCount, teamDocumentLimit); - } - - public long? CharacterCount { get; } - public long? CharacterLimit { get; } - public long? DocumentCount { get; } - public long? DocumentLimit { get; } - public long? TeamDocumentCount { get; } - public long? TeamDocumentLimit { get; } - } + public long? CharacterCount { get; } + public long? CharacterLimit { get; } + public long? DocumentCount { get; } + public long? DocumentLimit { get; } + public long? TeamDocumentCount { get; } + public long? TeamDocumentLimit { get; } } } diff --git a/src/DeepL/SentenceSplittingMode.cs b/src/DeepL/SentenceSplittingMode.cs deleted file mode 100644 index 0751579..0000000 --- a/src/DeepL/SentenceSplittingMode.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2022 DeepL SE (https://www.deepl.com) -// Use of this source code is governed by an MIT -// license that can be found in the LICENSE file. - -namespace DeepL { - /// Enum controlling how input translation text should be split into sentences. - public enum SentenceSplittingMode { - /// - /// Input translation text will be split into sentences using both newlines and punctuation, this is the default - /// behaviour. - /// - All, - - /// - /// Input text will not be split into sentences. This is advisable for applications where each input translation - /// text is only one sentence. - /// - Off, - - /// Input translation text will be split into sentences using only punctuation but not newlines. - NoNewlines - } -} diff --git a/src/DeepL/TextTranslateOptions.cs b/src/DeepL/TextTranslateOptions.cs deleted file mode 100644 index fe29df1..0000000 --- a/src/DeepL/TextTranslateOptions.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2022 DeepL SE (https://www.deepl.com) -// Use of this source code is governed by an MIT -// license that can be found in the LICENSE file. - -using System.Collections.Generic; -using DeepL.Model; - -namespace DeepL { - /// - /// Options to control text translation behaviour. These options may be provided to text - /// translate functions. - /// - public sealed class TextTranslateOptions { - /// Initializes a new object. - public TextTranslateOptions() { } - - /// Initializes a new object including the given glossary. - /// Glossary to use in translation. - public TextTranslateOptions(GlossaryInfo glossary) : this() { - GlossaryId = glossary.GlossaryId; - } - - /// Controls whether translations should lean toward formal or informal language. - /// This option is only applicable for target languages that support the formality option. - /// - public Formality Formality { get; set; } = Formality.Default; - - /// Specifies the ID of a glossary to use with the translation. - public string? GlossaryId { get; set; } - - /// Specifies a list of XML tags containing content that should not be translated. - public List IgnoreTags { get; } = new List(); - - /// Specifies a list of XML tags that should not be used to split text into sentences. - public List NonSplittingTags { get; } = new List(); - - /// Set to false to disable automatic tag detection, default is true. - public bool OutlineDetection { get; set; } = true; - - /// - /// Set to true to prevent the translation engine from correcting some formatting aspects, and instead leave the - /// formatting unchanged, default is false. - /// - public bool PreserveFormatting { get; set; } - - /// Specifies how input translation text should be split into sentences. - /// - public SentenceSplittingMode SentenceSplittingMode { get; set; } = SentenceSplittingMode.All; - - /// Specifies a list of XML tags that should be used to split text into sentences. - public List SplittingTags { get; } = new List(); - - /// Type of tags to parse before translation, only "xml" and "html" are currently available. - public string? TagHandling { get; set; } - } -} diff --git a/src/DeepL/Translator.cs b/src/DeepL/Translator.cs index 60d96b1..1a7b6ac 100644 --- a/src/DeepL/Translator.cs +++ b/src/DeepL/Translator.cs @@ -1,4 +1,4 @@ -// Copyright 2022 DeepL SE (https://www.deepl.com) +// Copyright 2022 DeepL SE (https://www.deepl.com) // Use of this source code is governed by an MIT // license that can be found in the LICENSE file. @@ -13,1079 +13,693 @@ using DeepL.Internal; using DeepL.Model; using DeepL.Model.Exceptions; +using DeepL.Model.Interfaces; +using DeepL.Model.Options; + +namespace DeepL; + +/// +/// Client for the DeepL API. To use the DeepL API, initialize an instance of this class using your DeepL +/// Authentication Key. All functions are thread-safe, aside from . +/// +public sealed class Translator : ITranslator { + /// Base URL for DeepL API Pro accounts. + private const string DeepLServerUrl = "https://api.deepl.com"; + + /// Base URL for DeepL API Free accounts. + private const string DeepLServerUrlFree = "https://api-free.deepl.com"; + + /// Internal class implementing HTTP requests. + private readonly DeepLClient _client; + + /// Initializes a new object using your authentication key. + /// + /// Authentication Key as found in your + /// DeepL API account. + /// + /// Additional options controlling Translator behaviour. + /// If authKey argument is null. + /// If authKey argument is empty. + /// + /// This function does not establish a connection to the DeepL API. To check connectivity, use + /// . + /// + public Translator(string authKey, TranslatorOptions? options = null) { + options ??= new TranslatorOptions(); + + if (authKey == null) { + throw new ArgumentNullException(nameof(authKey)); + } -namespace DeepL { - - public interface ITranslator : IDisposable { - - /// Retrieves the usage in the current billing period for this DeepL account. - /// The cancellation token to cancel operation. - /// object containing account usage information. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task GetUsageAsync(CancellationToken cancellationToken = default); - - /// Translate specified texts from source language into target language. - /// Texts to translate; must not be empty. - /// Language code of the input language, or null to use auto-detection. - /// Language code of the desired output language. - /// Extra influencing translation. - /// The cancellation token to cancel operation. - /// Texts translated into specified target language. - /// If any argument is invalid. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task TranslateTextAsync( - IEnumerable texts, - string? sourceLanguageCode, - string targetLanguageCode, - TextTranslateOptions? options = null, - CancellationToken cancellationToken = default); - - /// Translate specified text from source language into target language. - /// Text to translate; must not be empty. - /// Language code of the input language, or null to use auto-detection. - /// Language code of the desired output language. - /// influencing translation. - /// The cancellation token to cancel operation. - /// Text translated into specified target language. - /// If any argument is invalid. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task TranslateTextAsync( - string text, - string? sourceLanguageCode, - string targetLanguageCode, - TextTranslateOptions? options = null, - CancellationToken cancellationToken = default); - - /// - /// Translate document at specified input from source language to target language and - /// store the translated document at specified output . - /// - /// object containing path to input document. - /// object containing path to store translated document. - /// Language code of the input language, or null to use auto-detection. - /// Language code of the desired output language. - /// influencing translation. - /// The cancellation token to cancel operation. - /// If the output path is occupied. - /// - /// If cancellation was requested, or an error occurs while translating the document. If the document was uploaded - /// successfully, then the contains the document ID and - /// key that may be used to retrieve the document. - /// If cancellation was requested, the will be a - /// . - /// - Task TranslateDocumentAsync( - FileInfo inputFileInfo, - FileInfo outputFileInfo, - string? sourceLanguageCode, - string targetLanguageCode, - DocumentTranslateOptions? options = null, - CancellationToken cancellationToken = default); - - /// - /// Translate specified document content from source language to target language and store the translated document - /// content to specified stream. - /// - /// containing input document content. - /// Name of the input file. The file extension is used to determine file type. - /// to store translated document content. - /// Language code of the input language, or null to use auto-detection. - /// Language code of the desired output language. - /// influencing translation. - /// The cancellation token to cancel operation. - /// - /// If cancellation was requested, or an error occurs while translating the document. If the document was uploaded - /// successfully, then the contains the document ID and - /// key that may be used to retrieve the document. - /// If cancellation was requested, the will be a - /// . - /// - Task TranslateDocumentAsync( - Stream inputFile, - string inputFileName, - Stream outputFile, - string? sourceLanguageCode, - string targetLanguageCode, - DocumentTranslateOptions? options = null, - CancellationToken cancellationToken = default); - - /// - /// Upload document at specified input for translation from source language to target - /// language. - /// - /// object containing path to input document. - /// Language code of the input language, or null to use auto-detection. - /// Language code of the desired output language. - /// influencing translation. - /// The cancellation token to cancel operation. - /// object associated with the in-progress document translation. - /// If any argument is invalid. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task TranslateDocumentUploadAsync( - FileInfo inputFileInfo, - string? sourceLanguageCode, - string targetLanguageCode, - DocumentTranslateOptions? options = null, - CancellationToken cancellationToken = default); - - /// Upload document content with specified filename for translation from source language to target language. - /// containing input document content. - /// Name of the input file. The file extension is used to determine file type. - /// Language code of the input language, or null to use auto-detection. - /// Language code of the desired output language. - /// influencing translation. - /// The cancellation token to cancel operation. - /// object associated with the in-progress document translation. - /// If any argument is invalid. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task TranslateDocumentUploadAsync( - Stream inputFile, - string inputFileName, - string? sourceLanguageCode, - string targetLanguageCode, - DocumentTranslateOptions? options = null, - CancellationToken cancellationToken = default); - - /// - /// Retrieve the status of in-progress document translation associated with specified - /// . - /// - /// associated with an in-progress document translation. - /// The cancellation token to cancel operation. - /// object containing the document translation status. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task TranslateDocumentStatusAsync( - DocumentHandle handle, - CancellationToken cancellationToken = default); - - /// - /// Checks document translation status and asynchronously-waits until document translation is complete or fails - /// due to an error. - /// - /// associated with an in-progress document translation. - /// The cancellation token to cancel operation. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task TranslateDocumentWaitUntilDoneAsync( - DocumentHandle handle, - CancellationToken cancellationToken = default); - - /// - /// Downloads the resulting translated document associated with specified to the - /// specified . The for the document must have - /// == true. - /// - /// associated with an in-progress document translation. - /// object containing path to store translated document. - /// The cancellation token to cancel operation. - /// If the output path is occupied. - /// - /// If the document is not ready to be downloaded, that is, the document status - /// is not . - /// - /// - /// If any other error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task TranslateDocumentDownloadAsync( - DocumentHandle handle, - FileInfo outputFileInfo, - CancellationToken cancellationToken = default); - - /// - /// Downloads the resulting translated document associated with specified to the - /// specified . The for the document must have - /// == true. - /// - /// associated with an in-progress document translation. - /// to store translated document content. - /// The cancellation token to cancel operation. - /// - /// If the document is not ready to be downloaded, that is, the document status - /// is not . - /// - /// - /// If any other error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task TranslateDocumentDownloadAsync( - DocumentHandle handle, - Stream outputFile, - CancellationToken cancellationToken = default); - - /// Retrieves the list of supported translation source languages. - /// The cancellation token to cancel operation. - /// Array of objects representing the available translation source languages. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task GetSourceLanguagesAsync(CancellationToken cancellationToken = default); - - /// Retrieves the list of supported translation target languages. - /// The cancellation token to cancel operation. - /// Array of objects representing the available translation target languages. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task GetTargetLanguagesAsync(CancellationToken cancellationToken = default); - - /// - /// Retrieves the list of supported glossary language pairs. When creating glossaries, the source and target - /// language pair must match one of the available language pairs. - /// - /// The cancellation token to cancel operation. - /// Array of objects representing the available glossary language pairs. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task GetGlossaryLanguagesAsync(CancellationToken cancellationToken = default); - - /// - /// Creates a glossary in your DeepL account with the specified details and returns a - /// object with details about the newly created glossary. The glossary can be used in translations to override - /// translations for specific terms (words). The glossary source and target languages must match the languages of - /// translations for which it will be used. - /// - /// User-defined name to assign to the glossary; must not be empty. - /// Language code of the source terms language. - /// Language code of the target terms language. - /// Glossary entry list. - /// The cancellation token to cancel operation. - /// object with details about the newly created glossary. - /// If any argument is invalid. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task CreateGlossaryAsync( - string name, - string sourceLanguageCode, - string targetLanguageCode, - GlossaryEntries entries, - CancellationToken cancellationToken = default); - - /// - /// Creates a glossary in your DeepL account with the specified details and returns a - /// object with details about the newly created glossary. The glossary can be used in translations to override - /// translations for specific terms (words). The glossary source and target languages must match the languages of - /// translations for which it will be used. - /// - /// User-defined name to assign to the glossary; must not be empty. - /// Language code of the source terms language. - /// Language code of the target terms language. - /// containing CSV content. - /// The cancellation token to cancel operation. - /// object with details about the newly created glossary. - /// If any argument is invalid. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task CreateGlossaryFromCsvAsync( - string name, - string sourceLanguageCode, - string targetLanguageCode, - Stream csvFile, - CancellationToken cancellationToken = default); - - /// - /// Retrieves information about the glossary with the specified ID and returns a - /// object containing details. This does not retrieve the glossary entries; to retrieve entries use - /// - /// - /// ID of glossary to retrieve. - /// The cancellation token to cancel operation. - /// object with details about the specified glossary. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task GetGlossaryAsync( - string glossaryId, - CancellationToken cancellationToken = default); - - /// Asynchronously waits until the given glossary is ready to be used for translations. - /// ID of glossary to ensure ready. - /// The cancellation token to cancel operation. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task WaitUntilGlossaryReadyAsync( - string glossaryId, - CancellationToken cancellationToken = default); - - /// - /// Retrieves information about all glossaries and returns an array of objects - /// containing details. This does not retrieve the glossary entries; to retrieve entries use - /// - /// - /// The cancellation token to cancel operation. - /// Array of objects with details about each glossary. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task ListGlossariesAsync(CancellationToken cancellationToken = default); - - /// - /// Retrieves the entries containing within the glossary with the specified ID and returns them as a - /// . - /// - /// ID of glossary for which to retrieve entries. - /// The cancellation token to cancel operation. - /// containing entry pairs of the glossary. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task GetGlossaryEntriesAsync( - string glossaryId, - CancellationToken cancellationToken = default); - - /// - /// Retrieves the entries containing within the glossary and returns them as a . - /// - /// object corresponding to glossary for which to retrieve entries. - /// The cancellation token to cancel operation. - /// containing entry pairs of the glossary. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task GetGlossaryEntriesAsync( - GlossaryInfo glossary, - CancellationToken cancellationToken = default); - - /// Deletes the glossary with the specified ID. - /// ID of glossary to delete. - /// The cancellation token to cancel operation. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task DeleteGlossaryAsync( - string glossaryId, - CancellationToken cancellationToken = default); - - /// Deletes the specified glossary. - /// object corresponding to glossary to delete. - /// The cancellation token to cancel operation. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - Task DeleteGlossaryAsync( - GlossaryInfo glossary, - CancellationToken cancellationToken = default); - } - - /// - /// Client for the DeepL API. To use the DeepL API, initialize an instance of this class using your DeepL - /// Authentication Key. All functions are thread-safe, aside from . - /// - public sealed class Translator : ITranslator { - /// Base URL for DeepL API Pro accounts. - private const string DeepLServerUrl = "https://api.deepl.com"; - - /// Base URL for DeepL API Free accounts. - private const string DeepLServerUrlFree = "https://api-free.deepl.com"; - - /// Internal class implementing HTTP requests. - private readonly DeepLClient _client; - - /// Initializes a new object using your authentication key. - /// - /// Authentication Key as found in your - /// DeepL API account. - /// - /// Additional options controlling Translator behaviour. - /// If authKey argument is null. - /// If authKey argument is empty. - /// - /// This function does not establish a connection to the DeepL API. To check connectivity, use - /// . - /// - public Translator(string authKey, TranslatorOptions? options = null) { - options ??= new TranslatorOptions(); - - if (authKey == null) { - throw new ArgumentNullException(nameof(authKey)); - } - - authKey = authKey.Trim(); - - if (authKey.Length == 0) { - throw new ArgumentException($"{nameof(authKey)} is empty"); - } - - var serverUrl = new Uri( - options.ServerUrl ?? (AuthKeyIsFreeAccount(authKey) ? DeepLServerUrlFree : DeepLServerUrl)); - - var headers = new Dictionary(options.Headers, StringComparer.OrdinalIgnoreCase); + authKey = authKey.Trim(); - if (!headers.ContainsKey("User-Agent")) { - headers.Add("User-Agent", ConstructUserAgentString(options.sendPlatformInfo, options.appInfo)); - } + if (authKey.Length == 0) { + throw new ArgumentException($"{nameof(authKey)} is empty"); + } - if (!headers.ContainsKey("Authorization")) { - headers.Add("Authorization", $"DeepL-Auth-Key {authKey}"); - } + var serverUrl = new Uri( + options.ServerUrl ?? (AuthKeyIsFreeAccount(authKey) ? DeepLServerUrlFree : DeepLServerUrl)); - var clientFactory = options.ClientFactory ?? (() => - DeepLClient.CreateDefaultHttpClient( - options.PerRetryConnectionTimeout, - options.OverallConnectionTimeout, - options.MaximumNetworkRetries)); + var headers = new Dictionary(options.Headers, StringComparer.OrdinalIgnoreCase); - _client = new DeepLClient( - serverUrl, - clientFactory, - headers); + if (!headers.ContainsKey("User-Agent")) { + headers.Add("User-Agent", ConstructUserAgentString(options.sendPlatformInfo, options.appInfo)); } - /// Releases the unmanaged resources and disposes of the managed resources used by the . - public void Dispose() => _client.Dispose(); - - /// Retrieves the version string, with format MAJOR.MINOR.BUGFIX. - /// String containing the library version. - public static string Version() { - var version = Assembly.GetExecutingAssembly().GetCustomAttribute() - ?.InformationalVersion ?? ""; - return version; + if (!headers.ContainsKey("Authorization")) { + headers.Add("Authorization", $"DeepL-Auth-Key {authKey}"); } - /// - /// Determines if the given DeepL Authentication Key belongs to an API Free account or an API Pro account. - /// - /// - /// DeepL Authentication Key as found in your - /// DeepL API account. - /// - /// - /// true if the Authentication Key belongs to an API Free account, false if it belongs to an API Pro - /// account. - /// - public static bool AuthKeyIsFreeAccount(string authKey) => authKey.TrimEnd().EndsWith(":fx"); - - /// - public async Task GetUsageAsync(CancellationToken cancellationToken = default) { - using var responseMessage = await _client.ApiGetAsync("/v2/usage", cancellationToken).ConfigureAwait(false); - await DeepLClient.CheckStatusCodeAsync(responseMessage).ConfigureAwait(false); - var usageFields = await JsonUtils.DeserializeAsync(responseMessage) - .ConfigureAwait(false); - return new Usage(usageFields); - } + var clientFactory = options.ClientFactory ?? (() => + DeepLClient.CreateDefaultHttpClient( + options.PerRetryConnectionTimeout, + options.OverallConnectionTimeout, + options.MaximumNetworkRetries)); + _client = new DeepLClient( + serverUrl, + clientFactory, + headers); + } - /// - public async Task TranslateTextAsync( - IEnumerable texts, - string? sourceLanguageCode, - string targetLanguageCode, - TextTranslateOptions? options = null, - CancellationToken cancellationToken = default) { - var bodyParams = CreateHttpParams(sourceLanguageCode, targetLanguageCode, options); - var textParams = texts - .Where(text => text.Length > 0 ? true : throw new ArgumentException("text must not be empty")) - .Select(text => ("text", text)); - - using var responseMessage = await _client - .ApiPostAsync("/v2/translate", cancellationToken, bodyParams.Concat(textParams)).ConfigureAwait(false); - - await DeepLClient.CheckStatusCodeAsync(responseMessage).ConfigureAwait(false); - var translatedTexts = - await JsonUtils.DeserializeAsync(responseMessage).ConfigureAwait(false); - return translatedTexts.Translations; - } + /// Releases the unmanaged resources and disposes of the managed resources used by the . + public void Dispose() => _client.Dispose(); + /// Retrieves the version string, with format MAJOR.MINOR.BUGFIX. + /// String containing the library version. + public static string Version() { + var version = Assembly.GetExecutingAssembly().GetCustomAttribute() + ?.InformationalVersion ?? ""; + return version; + } - /// - public async Task TranslateTextAsync( - string text, - string? sourceLanguageCode, - string targetLanguageCode, - TextTranslateOptions? options = null, - CancellationToken cancellationToken = default) - => (await TranslateTextAsync( - new[] { text }, - sourceLanguageCode, - targetLanguageCode, - options, - cancellationToken) - .ConfigureAwait(false))[0]; - - - /// - public async Task TranslateDocumentAsync( - FileInfo inputFileInfo, - FileInfo outputFileInfo, - string? sourceLanguageCode, - string targetLanguageCode, - DocumentTranslateOptions? options = null, - CancellationToken cancellationToken = default) { - using var inputFile = inputFileInfo.OpenRead(); - using var outputFile = outputFileInfo.Open(FileMode.CreateNew, FileAccess.Write); - try { - await TranslateDocumentAsync( - inputFile, - inputFileInfo.Name, - outputFile, - sourceLanguageCode, - targetLanguageCode, - options, - cancellationToken).ConfigureAwait(false); - } catch { - try { - outputFileInfo.Delete(); - } catch { - // ignored - } + /// + /// Determines if the given DeepL Authentication Key belongs to an API Free account or an API Pro account. + /// + /// + /// DeepL Authentication Key as found in your + /// DeepL API account. + /// + /// + /// true if the Authentication Key belongs to an API Free account, false if it belongs to an API Pro + /// account. + /// + public static bool AuthKeyIsFreeAccount(string authKey) => authKey.TrimEnd().EndsWith(":fx"); + + /// + public async Task GetUsageAsync(CancellationToken cancellationToken = default) { + using var responseMessage = await _client.ApiGetAsync("/v2/usage", cancellationToken).ConfigureAwait(false); + await DeepLClient.CheckStatusCodeAsync(responseMessage).ConfigureAwait(false); + var usageFields = await JsonUtils.DeserializeAsync(responseMessage) + .ConfigureAwait(false); + return new Usage(usageFields); + } - throw; - } - } + /// + public async Task TranslateTextAsync( + IEnumerable texts, + string? sourceLanguageCode, + string targetLanguageCode, + TextTranslateOptions? options = null, + CancellationToken cancellationToken = default) { + var bodyParams = CreateHttpParams(sourceLanguageCode, targetLanguageCode, options); + var textParams = texts + .Where(text => text.Length > 0 ? true : throw new ArgumentException("text must not be empty")) + .Select(text => ("text", text)); + + using var responseMessage = await _client + .ApiPostAsync("/v2/translate", cancellationToken, bodyParams.Concat(textParams)).ConfigureAwait(false); + + await DeepLClient.CheckStatusCodeAsync(responseMessage).ConfigureAwait(false); + var translatedTexts = + await JsonUtils.DeserializeAsync(responseMessage).ConfigureAwait(false); + return translatedTexts.Translations; + } - /// - public async Task TranslateDocumentAsync( - Stream inputFile, - string inputFileName, - Stream outputFile, - string? sourceLanguageCode, - string targetLanguageCode, - DocumentTranslateOptions? options = null, - CancellationToken cancellationToken = default) { - DocumentHandle? handle = null; - try { - handle = await TranslateDocumentUploadAsync( - inputFile, - inputFileName, - sourceLanguageCode, - targetLanguageCode, - options, - cancellationToken) - .ConfigureAwait(false); - await TranslateDocumentWaitUntilDoneAsync(handle.Value, cancellationToken).ConfigureAwait(false); - await TranslateDocumentDownloadAsync(handle.Value, outputFile, cancellationToken).ConfigureAwait(false); - } catch (Exception exception) { - throw new DocumentTranslationException( - $"Error occurred during document translation: {exception.Message}", - exception, - handle); - } - } - /// - public async Task TranslateDocumentUploadAsync( - FileInfo inputFileInfo, - string? sourceLanguageCode, - string targetLanguageCode, - DocumentTranslateOptions? options = null, - CancellationToken cancellationToken = default) { - using var inputFileStream = inputFileInfo.OpenRead(); - return await TranslateDocumentUploadAsync( - inputFileStream, + /// + public async Task TranslateTextAsync( + string text, + string? sourceLanguageCode, + string targetLanguageCode, + TextTranslateOptions? options = null, + CancellationToken cancellationToken = default) + => (await TranslateTextAsync( + new[] { text }, + sourceLanguageCode, + targetLanguageCode, + options, + cancellationToken) + .ConfigureAwait(false))[0]; + + + /// + public async Task TranslateDocumentAsync( + FileInfo inputFileInfo, + FileInfo outputFileInfo, + string? sourceLanguageCode, + string targetLanguageCode, + DocumentTranslateOptions? options = null, + CancellationToken cancellationToken = default) { + using var inputFile = inputFileInfo.OpenRead(); + using var outputFile = outputFileInfo.Open(FileMode.CreateNew, FileAccess.Write); + try { + await TranslateDocumentAsync( + inputFile, inputFileInfo.Name, + outputFile, sourceLanguageCode, targetLanguageCode, options, cancellationToken).ConfigureAwait(false); + } catch { + try { + outputFileInfo.Delete(); + } catch { + // ignored + } + + throw; } + } - /// - public async Task TranslateDocumentUploadAsync( - Stream inputFile, - string inputFileName, - string? sourceLanguageCode, - string targetLanguageCode, - DocumentTranslateOptions? options = null, - CancellationToken cancellationToken = default) { - var bodyParams = CreateCommonHttpParams( - sourceLanguageCode, - targetLanguageCode, - options?.Formality, - options?.GlossaryId); - using var responseMessage = await _client.ApiUploadAsync( - "/v2/document/", - cancellationToken, - bodyParams, + /// + public async Task TranslateDocumentAsync( + Stream inputFile, + string inputFileName, + Stream outputFile, + string? sourceLanguageCode, + string targetLanguageCode, + DocumentTranslateOptions? options = null, + CancellationToken cancellationToken = default) { + DocumentHandle? handle = null; + try { + handle = await TranslateDocumentUploadAsync( inputFile, - inputFileName) + inputFileName, + sourceLanguageCode, + targetLanguageCode, + options, + cancellationToken) .ConfigureAwait(false); - await DeepLClient.CheckStatusCodeAsync(responseMessage).ConfigureAwait(false); - return await JsonUtils.DeserializeAsync(responseMessage).ConfigureAwait(false); + await TranslateDocumentWaitUntilDoneAsync(handle.Value, cancellationToken).ConfigureAwait(false); + await TranslateDocumentDownloadAsync(handle.Value, outputFile, cancellationToken).ConfigureAwait(false); + } catch (Exception exception) { + throw new DocumentTranslationException( + $"Error occurred during document translation: {exception.Message}", + exception, + handle); } + } - /// - public async Task TranslateDocumentStatusAsync( - DocumentHandle handle, - CancellationToken cancellationToken = default) { - var bodyParams = new (string Key, string Value)[] { ("document_key", handle.DocumentKey) }; - using var responseMessage = - await _client.ApiPostAsync( - $"/v2/document/{handle.DocumentId}", - cancellationToken, - bodyParams) - .ConfigureAwait(false); - await DeepLClient.CheckStatusCodeAsync(responseMessage).ConfigureAwait(false); - return await JsonUtils.DeserializeAsync(responseMessage).ConfigureAwait(false); - } + /// + public async Task TranslateDocumentUploadAsync( + FileInfo inputFileInfo, + string? sourceLanguageCode, + string targetLanguageCode, + DocumentTranslateOptions? options = null, + CancellationToken cancellationToken = default) { + using var inputFileStream = inputFileInfo.OpenRead(); + return await TranslateDocumentUploadAsync( + inputFileStream, + inputFileInfo.Name, + sourceLanguageCode, + targetLanguageCode, + options, + cancellationToken).ConfigureAwait(false); + } - /// - public async Task TranslateDocumentWaitUntilDoneAsync( - DocumentHandle handle, - CancellationToken cancellationToken = default) { - var status = await TranslateDocumentStatusAsync(handle, cancellationToken).ConfigureAwait(false); - while (status.Ok && !status.Done) { - await Task.Delay(CalculateDocumentWaitTime(status.SecondsRemaining), cancellationToken).ConfigureAwait(false); - status = await TranslateDocumentStatusAsync(handle, cancellationToken).ConfigureAwait(false); - } + /// + public async Task TranslateDocumentUploadAsync( + Stream inputFile, + string inputFileName, + string? sourceLanguageCode, + string targetLanguageCode, + DocumentTranslateOptions? options = null, + CancellationToken cancellationToken = default) { + var bodyParams = CreateCommonHttpParams( + sourceLanguageCode, + targetLanguageCode, + options?.Formality, + options?.GlossaryId); + + using var responseMessage = await _client.ApiUploadAsync( + "/v2/document/", + cancellationToken, + bodyParams, + inputFile, + inputFileName) + .ConfigureAwait(false); + await DeepLClient.CheckStatusCodeAsync(responseMessage).ConfigureAwait(false); + return await JsonUtils.DeserializeAsync(responseMessage).ConfigureAwait(false); + } - if (!status.Ok) { - throw new DeepLException(status.ErrorMessage ?? "Unknown error"); - } + /// + public async Task TranslateDocumentStatusAsync( + DocumentHandle handle, + CancellationToken cancellationToken = default) { + var bodyParams = new (string Key, string Value)[] { ("document_key", handle.DocumentKey) }; + using var responseMessage = + await _client.ApiPostAsync( + $"/v2/document/{handle.DocumentId}", + cancellationToken, + bodyParams) + .ConfigureAwait(false); + await DeepLClient.CheckStatusCodeAsync(responseMessage).ConfigureAwait(false); + return await JsonUtils.DeserializeAsync(responseMessage).ConfigureAwait(false); + } + + /// + public async Task TranslateDocumentWaitUntilDoneAsync( + DocumentHandle handle, + CancellationToken cancellationToken = default) { + var status = await TranslateDocumentStatusAsync(handle, cancellationToken).ConfigureAwait(false); + while (status.Ok && !status.Done) { + await Task.Delay(CalculateDocumentWaitTime(status.SecondsRemaining), cancellationToken).ConfigureAwait(false); + status = await TranslateDocumentStatusAsync(handle, cancellationToken).ConfigureAwait(false); } - /// - public async Task TranslateDocumentDownloadAsync( - DocumentHandle handle, - FileInfo outputFileInfo, - CancellationToken cancellationToken = default) { - using var outputFileStream = outputFileInfo.Open(FileMode.CreateNew, FileAccess.Write); + if (!status.Ok) { + throw new DeepLException(status.ErrorMessage ?? "Unknown error"); + } + } + + /// + public async Task TranslateDocumentDownloadAsync( + DocumentHandle handle, + FileInfo outputFileInfo, + CancellationToken cancellationToken = default) { + using var outputFileStream = outputFileInfo.Open(FileMode.CreateNew, FileAccess.Write); + try { + await TranslateDocumentDownloadAsync(handle, outputFileStream, cancellationToken).ConfigureAwait(false); + } catch { try { - await TranslateDocumentDownloadAsync(handle, outputFileStream, cancellationToken).ConfigureAwait(false); + outputFileInfo.Delete(); } catch { - try { - outputFileInfo.Delete(); - } catch { - // ignored - } - - throw; + // ignored } - } - /// - public async Task TranslateDocumentDownloadAsync( - DocumentHandle handle, - Stream outputFile, - CancellationToken cancellationToken = default) { - var bodyParams = new (string Key, string Value)[] { ("document_key", handle.DocumentKey) }; - using var responseMessage = await _client.ApiPostAsync( - $"/v2/document/{handle.DocumentId}/result", - cancellationToken, - bodyParams) - .ConfigureAwait(false); - - await DeepLClient.CheckStatusCodeAsync(responseMessage, downloadingDocument: true).ConfigureAwait(false); - await responseMessage.Content.CopyToAsync(outputFile).ConfigureAwait(false); + throw; } + } - /// - public async Task GetSourceLanguagesAsync(CancellationToken cancellationToken = default) => - await GetLanguagesAsync(false, cancellationToken).ConfigureAwait(false); - - /// - public async Task GetTargetLanguagesAsync(CancellationToken cancellationToken = default) => - await GetLanguagesAsync(true, cancellationToken).ConfigureAwait(false); + /// + public async Task TranslateDocumentDownloadAsync( + DocumentHandle handle, + Stream outputFile, + CancellationToken cancellationToken = default) { + var bodyParams = new (string Key, string Value)[] { ("document_key", handle.DocumentKey) }; + using var responseMessage = await _client.ApiPostAsync( + $"/v2/document/{handle.DocumentId}/result", + cancellationToken, + bodyParams) + .ConfigureAwait(false); + + await DeepLClient.CheckStatusCodeAsync(responseMessage, downloadingDocument: true).ConfigureAwait(false); + await responseMessage.Content.CopyToAsync(outputFile).ConfigureAwait(false); + } - /// - public async Task GetGlossaryLanguagesAsync(CancellationToken cancellationToken = default) { - using var responseMessage = await _client - .ApiGetAsync("/v2/glossary-language-pairs", cancellationToken).ConfigureAwait(false); + /// + public async Task GetSourceLanguagesAsync(CancellationToken cancellationToken = default) => + await GetLanguagesAsync(false, cancellationToken).ConfigureAwait(false); - await DeepLClient.CheckStatusCodeAsync(responseMessage).ConfigureAwait(false); - var languages = await JsonUtils.DeserializeAsync(responseMessage) - .ConfigureAwait(false); - return languages.GlossaryLanguagePairs; - } + /// + public async Task GetTargetLanguagesAsync(CancellationToken cancellationToken = default) => + await GetLanguagesAsync(true, cancellationToken).ConfigureAwait(false); - /// - public async Task CreateGlossaryAsync( - string name, - string sourceLanguageCode, - string targetLanguageCode, - GlossaryEntries entries, - CancellationToken cancellationToken = default) => - await CreateGlossaryInternalAsync( - name, - sourceLanguageCode, - targetLanguageCode, - "tsv", - entries.ToTsv(), - cancellationToken).ConfigureAwait(false); - - /// - public async Task CreateGlossaryFromCsvAsync( - string name, - string sourceLanguageCode, - string targetLanguageCode, - Stream csvFile, - CancellationToken cancellationToken = default) => - await CreateGlossaryInternalAsync( - name, - sourceLanguageCode, - targetLanguageCode, - "csv", - await new StreamReader(csvFile).ReadToEndAsync().ConfigureAwait(false), - cancellationToken).ConfigureAwait(false); - - /// - public async Task GetGlossaryAsync( - string glossaryId, - CancellationToken cancellationToken = default) { - using var responseMessage = - await _client.ApiGetAsync($"/v2/glossaries/{glossaryId}", cancellationToken) - .ConfigureAwait(false); - - await DeepLClient.CheckStatusCodeAsync(responseMessage, true).ConfigureAwait(false); - return await JsonUtils.DeserializeAsync(responseMessage).ConfigureAwait(false); - } + /// + public async Task GetGlossaryLanguagesAsync(CancellationToken cancellationToken = default) { + using var responseMessage = await _client + .ApiGetAsync("/v2/glossary-language-pairs", cancellationToken).ConfigureAwait(false); - /// - public async Task WaitUntilGlossaryReadyAsync( - string glossaryId, - CancellationToken cancellationToken = default) { - var info = await GetGlossaryAsync(glossaryId, cancellationToken).ConfigureAwait(false); - while (!info.Ready) { - await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken).ConfigureAwait(false); - info = await GetGlossaryAsync(glossaryId, cancellationToken).ConfigureAwait(false); - } + await DeepLClient.CheckStatusCodeAsync(responseMessage).ConfigureAwait(false); + var languages = await JsonUtils.DeserializeAsync(responseMessage) + .ConfigureAwait(false); + return languages.GlossaryLanguagePairs; + } - return info; - } + /// + public async Task CreateGlossaryAsync( + string name, + string sourceLanguageCode, + string targetLanguageCode, + GlossaryEntries entries, + CancellationToken cancellationToken = default) => + await CreateGlossaryInternalAsync( + name, + sourceLanguageCode, + targetLanguageCode, + "tsv", + entries.ToTsv(), + cancellationToken).ConfigureAwait(false); - /// - public async Task ListGlossariesAsync(CancellationToken cancellationToken = default) { - using var responseMessage = - await _client.ApiGetAsync("/v2/glossaries", cancellationToken).ConfigureAwait(false); + /// + public async Task CreateGlossaryFromCsvAsync( + string name, + string sourceLanguageCode, + string targetLanguageCode, + Stream csvFile, + CancellationToken cancellationToken = default) => + await CreateGlossaryInternalAsync( + name, + sourceLanguageCode, + targetLanguageCode, + "csv", + await new StreamReader(csvFile).ReadToEndAsync().ConfigureAwait(false), + cancellationToken).ConfigureAwait(false); - await DeepLClient.CheckStatusCodeAsync(responseMessage, true).ConfigureAwait(false); - return (await JsonUtils.DeserializeAsync(responseMessage).ConfigureAwait(false)).Glossaries; - } + /// + public async Task GetGlossaryAsync( + string glossaryId, + CancellationToken cancellationToken = default) { + using var responseMessage = + await _client.ApiGetAsync($"/v2/glossaries/{glossaryId}", cancellationToken) + .ConfigureAwait(false); - /// - public async Task GetGlossaryEntriesAsync( - string glossaryId, - CancellationToken cancellationToken = default) { - using var responseMessage = await _client.ApiGetAsync( - $"/v2/glossaries/{glossaryId}/entries", - cancellationToken, - acceptHeader: "text/tab-separated-values").ConfigureAwait(false); - - await DeepLClient.CheckStatusCodeAsync(responseMessage, true).ConfigureAwait(false); - var contentTsv = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false); - return GlossaryEntries.FromTsv(contentTsv, true); - } + await DeepLClient.CheckStatusCodeAsync(responseMessage, true).ConfigureAwait(false); + return await JsonUtils.DeserializeAsync(responseMessage).ConfigureAwait(false); + } - /// - public async Task GetGlossaryEntriesAsync( - GlossaryInfo glossary, - CancellationToken cancellationToken = default) => - await GetGlossaryEntriesAsync(glossary.GlossaryId, cancellationToken).ConfigureAwait(false); - - /// - public async Task DeleteGlossaryAsync( - string glossaryId, - CancellationToken cancellationToken = default) { - using var responseMessage = - await _client.ApiDeleteAsync($"/v2/glossaries/{glossaryId}", cancellationToken) - .ConfigureAwait(false); - - await DeepLClient.CheckStatusCodeAsync(responseMessage, true).ConfigureAwait(false); + /// + public async Task WaitUntilGlossaryReadyAsync( + string glossaryId, + CancellationToken cancellationToken = default) { + var info = await GetGlossaryAsync(glossaryId, cancellationToken).ConfigureAwait(false); + while (!info.Ready) { + await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken).ConfigureAwait(false); + info = await GetGlossaryAsync(glossaryId, cancellationToken).ConfigureAwait(false); } - /// - public async Task DeleteGlossaryAsync( - GlossaryInfo glossary, - CancellationToken cancellationToken = default) => - await DeleteGlossaryAsync(glossary.GlossaryId, cancellationToken).ConfigureAwait(false); - - /// Internal function to retrieve available languages. - /// true to retrieve target languages, false to retrieve source languages. - /// The cancellation token to cancel operation. - /// Array of objects containing information about the available languages. - /// - /// If any error occurs while communicating with the DeepL API, a - /// or a derived class will be thrown. - /// - private async Task GetLanguagesAsync( - bool target, - CancellationToken cancellationToken = default) { - var queryParams = new (string Key, string Value)[] { ("type", target ? "target" : "source") }; - using var responseMessage = - await _client.ApiGetAsync("/v2/languages", cancellationToken, queryParams) - .ConfigureAwait(false); - - await DeepLClient.CheckStatusCodeAsync(responseMessage).ConfigureAwait(false); - return await JsonUtils.DeserializeAsync(responseMessage).ConfigureAwait(false); - } + return info; + } - /// - /// Builds the user-agent string we want to send to the API with every request. This string contains - /// basic information about the client environment, such as the deepl client library version, operating - /// system and language runtime version. - /// - /// - /// true to send platform information with every API request (default), - /// false to only send the library version. - /// - /// - /// Name and version of the application using this library. Ignored if null. - /// - /// Enumerable of tuples containing the parameters to include in HTTP request. - private String ConstructUserAgentString(bool sendPlatformInfo = true, AppInfo? appInfo = null) { - var platformInfoString = $"deepl-dotnet/{Version()}"; - if (sendPlatformInfo) { - var osDescription = System.Runtime.InteropServices.RuntimeInformation.OSDescription; - var clrVersion = Environment.Version.ToString(); - platformInfoString += $" ({osDescription}) dotnet-clr/{clrVersion}"; - } - if (appInfo != null) { - platformInfoString += $" {appInfo?.AppName}/{appInfo?.AppVersion}"; - } - return platformInfoString; - } + /// + public async Task ListGlossariesAsync(CancellationToken cancellationToken = default) { + using var responseMessage = + await _client.ApiGetAsync("/v2/glossaries", cancellationToken).ConfigureAwait(false); - /// - /// Checks the specified languages and options are valid, and returns an enumerable of tuples containing the parameters - /// to include in HTTP request. - /// - /// - /// Language code of translation source language, or null if auto-detection should be - /// used. - /// - /// Language code of translation target language. - /// Extra influencing translation. - /// Enumerable of tuples containing the parameters to include in HTTP request. - /// If the specified languages or options are invalid. - private static IEnumerable<(string Key, string Value)> CreateHttpParams( - string? sourceLanguageCode, - string targetLanguageCode, - TextTranslateOptions? options) { - - var bodyParams = CreateCommonHttpParams( - sourceLanguageCode, - targetLanguageCode, - options?.Formality, - options?.GlossaryId); + await DeepLClient.CheckStatusCodeAsync(responseMessage, true).ConfigureAwait(false); + return (await JsonUtils.DeserializeAsync(responseMessage).ConfigureAwait(false)).Glossaries; + } - if (options == null) { - return bodyParams; - } + /// + public async Task GetGlossaryEntriesAsync( + string glossaryId, + CancellationToken cancellationToken = default) { + using var responseMessage = await _client.ApiGetAsync( + $"/v2/glossaries/{glossaryId}/entries", + cancellationToken, + acceptHeader: "text/tab-separated-values").ConfigureAwait(false); + + await DeepLClient.CheckStatusCodeAsync(responseMessage, true).ConfigureAwait(false); + var contentTsv = await responseMessage.Content.ReadAsStringAsync().ConfigureAwait(false); + return GlossaryEntries.FromTsv(contentTsv, true); + } - if (options.SentenceSplittingMode != SentenceSplittingMode.All) { - bodyParams.Add( - ("split_sentences", options.SentenceSplittingMode == SentenceSplittingMode.Off ? "0" : "nonewlines")); - } + /// + public async Task GetGlossaryEntriesAsync( + GlossaryInfo glossary, + CancellationToken cancellationToken = default) => + await GetGlossaryEntriesAsync(glossary.GlossaryId, cancellationToken).ConfigureAwait(false); + + /// + public async Task DeleteGlossaryAsync( + string glossaryId, + CancellationToken cancellationToken = default) { + using var responseMessage = + await _client.ApiDeleteAsync($"/v2/glossaries/{glossaryId}", cancellationToken) + .ConfigureAwait(false); + + await DeepLClient.CheckStatusCodeAsync(responseMessage, true).ConfigureAwait(false); + } - if (options.PreserveFormatting) { - bodyParams.Add(("preserve_formatting", "1")); - } + /// + public async Task DeleteGlossaryAsync( + GlossaryInfo glossary, + CancellationToken cancellationToken = default) => + await DeleteGlossaryAsync(glossary.GlossaryId, cancellationToken).ConfigureAwait(false); + + /// Internal function to retrieve available languages. + /// true to retrieve target languages, false to retrieve source languages. + /// The cancellation token to cancel operation. + /// Array of objects containing information about the available languages. + /// + /// If any error occurs while communicating with the DeepL API, a + /// or a derived class will be thrown. + /// + private async Task GetLanguagesAsync( + bool target, + CancellationToken cancellationToken = default) { + var queryParams = new (string Key, string Value)[] { ("type", target ? "target" : "source") }; + using var responseMessage = + await _client.ApiGetAsync("/v2/languages", cancellationToken, queryParams) + .ConfigureAwait(false); + + await DeepLClient.CheckStatusCodeAsync(responseMessage).ConfigureAwait(false); + return await JsonUtils.DeserializeAsync(responseMessage).ConfigureAwait(false); + } - if (options.TagHandling != null) { - bodyParams.Add(("tag_handling", options.TagHandling)); - } + /// + /// Builds the user-agent string we want to send to the API with every request. This string contains + /// basic information about the client environment, such as the deepl client library version, operating + /// system and language runtime version. + /// + /// + /// true to send platform information with every API request (default), + /// false to only send the library version. + /// + /// + /// Name and version of the application using this library. Ignored if null. + /// + /// Enumerable of tuples containing the parameters to include in HTTP request. + private String ConstructUserAgentString(bool sendPlatformInfo = true, AppInfo? appInfo = null) { + var platformInfoString = $"deepl-dotnet/{Version()}"; + if (sendPlatformInfo) { + var osDescription = System.Runtime.InteropServices.RuntimeInformation.OSDescription; + var clrVersion = Environment.Version.ToString(); + platformInfoString += $" ({osDescription}) dotnet-clr/{clrVersion}"; + } + if (appInfo != null) { + platformInfoString += $" {appInfo?.AppName}/{appInfo?.AppVersion}"; + } + return platformInfoString; + } - if (!options.OutlineDetection) { - bodyParams.Add(("outline_detection", "0")); - } + /// + /// Checks the specified languages and options are valid, and returns an enumerable of tuples containing the parameters + /// to include in HTTP request. + /// + /// + /// Language code of translation source language, or null if auto-detection should be + /// used. + /// + /// Language code of translation target language. + /// Extra influencing translation. + /// Enumerable of tuples containing the parameters to include in HTTP request. + /// If the specified languages or options are invalid. + private static IEnumerable<(string Key, string Value)> CreateHttpParams( + string? sourceLanguageCode, + string targetLanguageCode, + TextTranslateOptions? options) { + + var bodyParams = CreateCommonHttpParams( + sourceLanguageCode, + targetLanguageCode, + options?.Formality, + options?.GlossaryId); + + if (options == null) { + return bodyParams; + } - if (options.NonSplittingTags.Count > 0) { - bodyParams.Add(("non_splitting_tags", string.Join(",", options.NonSplittingTags))); - } + if (options.SentenceSplittingMode != SentenceSplittingMode.All) { + bodyParams.Add( + ("split_sentences", options.SentenceSplittingMode == SentenceSplittingMode.Off ? "0" : "nonewlines")); + } - if (options.SplittingTags.Count > 0) { - bodyParams.Add(("splitting_tags", string.Join(",", options.SplittingTags))); - } + if (options.PreserveFormatting) { + bodyParams.Add(("preserve_formatting", "1")); + } - if (options.IgnoreTags.Count > 0) { - bodyParams.Add(("ignore_tags", string.Join(",", options.IgnoreTags))); - } + if (options.TagHandling != null) { + bodyParams.Add(("tag_handling", options.TagHandling)); + } - return bodyParams; + if (!options.OutlineDetection) { + bodyParams.Add(("outline_detection", "0")); } - /// - /// Checks the specified languages and options are valid, and returns a list of tuples containing the parameters - /// to include in HTTP request. - /// - /// - /// Language code of translation source language, or null if auto-detection should be - /// used. - /// - /// Language code of translation target language. - /// Formality option for translation. - /// Optional ID of glossary to use for translation. - /// List of tuples containing the parameters to include in HTTP request. - /// If the specified languages or options are invalid. - private static List<(string Key, string Value)> CreateCommonHttpParams( - string? sourceLanguageCode, - string targetLanguageCode, - Formality? formality, - string? glossaryId) { - targetLanguageCode = LanguageCode.Standardize(targetLanguageCode); - sourceLanguageCode = sourceLanguageCode == null ? null : LanguageCode.Standardize(sourceLanguageCode); - - CheckValidLanguages(sourceLanguageCode, targetLanguageCode); - - var bodyParams = new List<(string Key, string Value)> { ("target_lang", targetLanguageCode) }; - if (sourceLanguageCode != null) { - bodyParams.Add(("source_lang", sourceLanguageCode)); - } + if (options.NonSplittingTags.Count > 0) { + bodyParams.Add(("non_splitting_tags", string.Join(",", options.NonSplittingTags))); + } - if (glossaryId != null) { - if (sourceLanguageCode == null) { - throw new ArgumentException($"{nameof(sourceLanguageCode)} is required if using a glossary"); - } + if (options.SplittingTags.Count > 0) { + bodyParams.Add(("splitting_tags", string.Join(",", options.SplittingTags))); + } - bodyParams.Add(("glossary_id", glossaryId)); - } + if (options.IgnoreTags.Count > 0) { + bodyParams.Add(("ignore_tags", string.Join(",", options.IgnoreTags))); + } - switch (formality) { - case null: - case Formality.Default: - break; - case Formality.Less: - bodyParams.Add(("formality", "less")); - break; - case Formality.More: - bodyParams.Add(("formality", "more")); - break; - case Formality.PreferLess: - bodyParams.Add(("formality", "prefer_less")); - break; - case Formality.PreferMore: - bodyParams.Add(("formality", "prefer_more")); - break; - default: - throw new ArgumentException($"{nameof(formality)} value is out of range"); - } + return bodyParams; + } - return bodyParams; + /// + /// Checks the specified languages and options are valid, and returns a list of tuples containing the parameters + /// to include in HTTP request. + /// + /// + /// Language code of translation source language, or null if auto-detection should be + /// used. + /// + /// Language code of translation target language. + /// Formality option for translation. + /// Optional ID of glossary to use for translation. + /// List of tuples containing the parameters to include in HTTP request. + /// If the specified languages or options are invalid. + private static List<(string Key, string Value)> CreateCommonHttpParams( + string? sourceLanguageCode, + string targetLanguageCode, + Formality? formality, + string? glossaryId) { + targetLanguageCode = LanguageCode.Standardize(targetLanguageCode); + sourceLanguageCode = sourceLanguageCode == null ? null : LanguageCode.Standardize(sourceLanguageCode); + + CheckValidLanguages(sourceLanguageCode, targetLanguageCode); + + var bodyParams = new List<(string Key, string Value)> { ("target_lang", targetLanguageCode) }; + if (sourceLanguageCode != null) { + bodyParams.Add(("source_lang", sourceLanguageCode)); } - /// Checks the specified source and target language are valid, and throws an exception if not. - /// Language code of translation source language, or null if auto-detection is used. - /// Language code of translation target language. - /// If source or target language code are not valid. - private static void CheckValidLanguages(string? sourceLanguageCode, string targetLanguageCode) { - if (sourceLanguageCode is { Length: 0 }) { - throw new ArgumentException($"{nameof(sourceLanguageCode)} must not be empty"); + if (glossaryId != null) { + if (sourceLanguageCode == null) { + throw new ArgumentException($"{nameof(sourceLanguageCode)} is required if using a glossary"); } - if (targetLanguageCode.Length == 0) { - throw new ArgumentException($"{nameof(targetLanguageCode)} must not be empty"); - } + bodyParams.Add(("glossary_id", glossaryId)); + } - switch (targetLanguageCode) { - case "en": - throw new ArgumentException( - $"{nameof(targetLanguageCode)}=\"en\" is deprecated, please use \"en-GB\" or \"en-US\" instead"); - case "pt": - throw new ArgumentException( - $"{nameof(targetLanguageCode)}=\"pt\" is deprecated, please use \"pt-PT\" or \"pt-BR\" instead"); - } + switch (formality) { + case null: + case Formality.Default: + break; + case Formality.Less: + bodyParams.Add(("formality", "less")); + break; + case Formality.More: + bodyParams.Add(("formality", "more")); + break; + case Formality.PreferLess: + bodyParams.Add(("formality", "prefer_less")); + break; + case Formality.PreferMore: + bodyParams.Add(("formality", "prefer_more")); + break; + default: + throw new ArgumentException($"{nameof(formality)} value is out of range"); } - /// - /// Determines recommended time to wait before checking document translation again, using an optional hint of - /// seconds remaining. - /// - /// Optional hint of the number of seconds remaining. - /// to wait. - private static TimeSpan CalculateDocumentWaitTime(int? hintSecondsRemaining) { - // hintSecondsRemaining is currently unreliable, so just poll equidistantly - const int POLLING_TIME_SECS = 5; - return TimeSpan.FromSeconds(POLLING_TIME_SECS); + return bodyParams; + } + + /// Checks the specified source and target language are valid, and throws an exception if not. + /// Language code of translation source language, or null if auto-detection is used. + /// Language code of translation target language. + /// If source or target language code are not valid. + private static void CheckValidLanguages(string? sourceLanguageCode, string targetLanguageCode) { + if (sourceLanguageCode is { Length: 0 }) { + throw new ArgumentException($"{nameof(sourceLanguageCode)} must not be empty"); } - /// Creates a glossary with given details. - private async Task CreateGlossaryInternalAsync( - string name, - string sourceLanguageCode, - string targetLanguageCode, - string entriesFormat, - string entries, - CancellationToken cancellationToken) { - if (name.Length == 0) { - throw new ArgumentException($"Parameter {nameof(name)} must not be empty"); - } + if (targetLanguageCode.Length == 0) { + throw new ArgumentException($"{nameof(targetLanguageCode)} must not be empty"); + } - sourceLanguageCode = LanguageCode.RemoveRegionalVariant(sourceLanguageCode); - targetLanguageCode = LanguageCode.RemoveRegionalVariant(targetLanguageCode); + switch (targetLanguageCode) { + case "en": + throw new ArgumentException( + $"{nameof(targetLanguageCode)}=\"en\" is deprecated, please use \"en-GB\" or \"en-US\" instead"); + case "pt": + throw new ArgumentException( + $"{nameof(targetLanguageCode)}=\"pt\" is deprecated, please use \"pt-PT\" or \"pt-BR\" instead"); + } + } - var bodyParams = new (string Key, string Value)[] { - ("name", name), ("source_lang", sourceLanguageCode), ("target_lang", targetLanguageCode), - ("entries_format", entriesFormat), ("entries", entries) - }; - using var responseMessage = - await _client.ApiPostAsync("/v2/glossaries", cancellationToken, bodyParams).ConfigureAwait(false); + /// + /// Determines recommended time to wait before checking document translation again, using an optional hint of + /// seconds remaining. + /// + /// Optional hint of the number of seconds remaining. + /// to wait. + private static TimeSpan CalculateDocumentWaitTime(int? hintSecondsRemaining) { + // hintSecondsRemaining is currently unreliable, so just poll equidistantly + const int POLLING_TIME_SECS = 5; + return TimeSpan.FromSeconds(POLLING_TIME_SECS); + } - await DeepLClient.CheckStatusCodeAsync(responseMessage, true).ConfigureAwait(false); - return await JsonUtils.DeserializeAsync(responseMessage).ConfigureAwait(false); + /// Creates a glossary with given details. + private async Task CreateGlossaryInternalAsync( + string name, + string sourceLanguageCode, + string targetLanguageCode, + string entriesFormat, + string entries, + CancellationToken cancellationToken) { + if (name.Length == 0) { + throw new ArgumentException($"Parameter {nameof(name)} must not be empty"); } - /// Class used for JSON-deserialization of text translate results. - private readonly struct TextTranslateResult { - /// Initializes a new instance of , used for JSON deserialization. - [JsonConstructor] - public TextTranslateResult(TextResult[] translations) { - Translations = translations; - } + sourceLanguageCode = LanguageCode.RemoveRegionalVariant(sourceLanguageCode); + targetLanguageCode = LanguageCode.RemoveRegionalVariant(targetLanguageCode); + + var bodyParams = new (string Key, string Value)[] { + ("name", name), ("source_lang", sourceLanguageCode), ("target_lang", targetLanguageCode), + ("entries_format", entriesFormat), ("entries", entries) + }; + using var responseMessage = + await _client.ApiPostAsync("/v2/glossaries", cancellationToken, bodyParams).ConfigureAwait(false); - /// Array of objects holding text translation results. - public TextResult[] Translations { get; } + await DeepLClient.CheckStatusCodeAsync(responseMessage, true).ConfigureAwait(false); + return await JsonUtils.DeserializeAsync(responseMessage).ConfigureAwait(false); + } + + /// Class used for JSON-deserialization of text translate results. + private readonly struct TextTranslateResult { + /// Initializes a new instance of , used for JSON deserialization. + [JsonConstructor] + public TextTranslateResult(TextResult[] translations) { + Translations = translations; } - /// Class used for JSON-deserialization of glossary list results. - private readonly struct GlossaryListResult { - /// Initializes a new instance of , used for JSON deserialization. - [JsonConstructor] - public GlossaryListResult(GlossaryInfo[] glossaries) { - Glossaries = glossaries; - } + /// Array of objects holding text translation results. + public TextResult[] Translations { get; } + } - /// Array of objects holding glossary information. - public GlossaryInfo[] Glossaries { get; } + /// Class used for JSON-deserialization of glossary list results. + private readonly struct GlossaryListResult { + /// Initializes a new instance of , used for JSON deserialization. + [JsonConstructor] + public GlossaryListResult(GlossaryInfo[] glossaries) { + Glossaries = glossaries; } - /// Class used for JSON-deserialization of results of supported languages for glossaries. - private readonly struct GlossaryLanguageListResult { - /// Initializes a new instance of , used for JSON deserialization. - [JsonConstructor] - public GlossaryLanguageListResult(GlossaryLanguagePair[] glossaryLanguagePairs) { - GlossaryLanguagePairs = glossaryLanguagePairs; - } + /// Array of objects holding glossary information. + public GlossaryInfo[] Glossaries { get; } + } - /// Array of objects holding supported glossary language pairs. - [JsonPropertyName("supported_languages")] - public GlossaryLanguagePair[] GlossaryLanguagePairs { get; } + /// Class used for JSON-deserialization of results of supported languages for glossaries. + private readonly struct GlossaryLanguageListResult { + /// Initializes a new instance of , used for JSON deserialization. + [JsonConstructor] + public GlossaryLanguageListResult(GlossaryLanguagePair[] glossaryLanguagePairs) { + GlossaryLanguagePairs = glossaryLanguagePairs; } + + /// Array of objects holding supported glossary language pairs. + [JsonPropertyName("supported_languages")] + public GlossaryLanguagePair[] GlossaryLanguagePairs { get; } } } diff --git a/src/DeepL/TranslatorOptions.cs b/src/DeepL/TranslatorOptions.cs deleted file mode 100644 index d6d9d78..0000000 --- a/src/DeepL/TranslatorOptions.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2022 DeepL SE (https://www.deepl.com) -// Use of this source code is governed by an MIT -// license that can be found in the LICENSE file. - -using System; -using System.Collections.Generic; -using System.Net.Http; - -namespace DeepL { - /// Class containing containing options controlling behaviour. - public sealed class TranslatorOptions { - /// - /// HTTP headers attached to every HTTP request. By default no extra headers are used. Note that during - /// initialization headers for Authorization and User-Agent are added, unless they are - /// overridden in this option. - /// - public Dictionary Headers { get; set; } = new Dictionary(); - - /// - /// The maximum number of failed attempts that will retry, per request. By default 5 retries - /// are made. Note: only errors due to transient conditions are retried. Only used if is - /// unset. - /// - public int MaximumNetworkRetries { get; set; } = 5; - - /// - /// Connection timeout for HTTP requests, including all retries, the default is 100 seconds. Only used if - /// is unset. - /// - public TimeSpan OverallConnectionTimeout { get; set; } = TimeSpan.FromSeconds(100); - - /// - /// Connection timeout used for each HTTP request retry, the default is 10 seconds. Only used if - /// is unset. - /// - public TimeSpan PerRetryConnectionTimeout { get; set; } = TimeSpan.FromSeconds(10); - - /// - /// The base URL for DeepL's API that may be overridden for testing purposes. By default the correct DeepL API URL - /// is selected based on the user account type (free or paid). - /// - public string? ServerUrl { get; set; } - - /// - /// Factory function returning an to be used by for HTTP requests, - /// and a flag whether to call in . - /// Override this function to provide your own . Note that overriding this function will disable - /// the built-in retrying of failed-requests, so you must provide your own retry policy. - /// - public Func? ClientFactory { get; set; } - - public bool sendPlatformInfo { get; set; } = true; - - public AppInfo? appInfo { get; set; } - } - - /// - /// Struct containing an to be used by and flag whether it - /// should be disposed in . - /// - public struct HttpClientAndDisposeFlag { - /// - /// used by for all requests to DeepL API. - /// - public HttpClient HttpClient { get; set; } - - /// - /// true if the provided should be disposed of by ; - /// false if you - /// intend to reuse it. - /// - public bool DisposeClient { get; set; } - } - - /// - /// Represents the identifying information of an application that uses this library, its name and version. - /// Example: AppName = "myDotNetCustomerChatApp", AppVersion = "1.2.3" - /// - public struct AppInfo { - /// - /// Name of the application using this client library - /// - public string AppName { get; set; } - /// - /// Version of the application using this client library - /// - public string AppVersion { get; set; } - } -} diff --git a/src/DeepLTests/BaseDeepLTest.cs b/src/DeepLTests/BaseDeepLTest.cs index 34ac792..b5ba98e 100644 --- a/src/DeepLTests/BaseDeepLTest.cs +++ b/src/DeepLTests/BaseDeepLTest.cs @@ -9,6 +9,8 @@ using System.Threading; using System.Threading.Tasks; using DeepL; +using DeepL.Model; +using DeepL.Model.Options; using Xunit; namespace DeepLTests { diff --git a/src/DeepLTests/GeneralTest.cs b/src/DeepLTests/GeneralTest.cs index 9011245..24b3461 100644 --- a/src/DeepLTests/GeneralTest.cs +++ b/src/DeepLTests/GeneralTest.cs @@ -7,7 +7,9 @@ using System.Net.Http; using System.Threading.Tasks; using DeepL; +using DeepL.Model; using DeepL.Model.Exceptions; +using DeepL.Model.Options; using Xunit; using Xunit.Abstractions; diff --git a/src/DeepLTests/GlossaryTest.cs b/src/DeepLTests/GlossaryTest.cs index 41426a2..f1fd2dc 100644 --- a/src/DeepLTests/GlossaryTest.cs +++ b/src/DeepLTests/GlossaryTest.cs @@ -10,6 +10,7 @@ using DeepL; using DeepL.Model; using DeepL.Model.Exceptions; +using DeepL.Model.Options; using Xunit; namespace DeepLTests { diff --git a/src/DeepLTests/SubstitutionExamples.cs b/src/DeepLTests/SubstitutionExamples.cs index 7cf27d1..54f8425 100644 --- a/src/DeepLTests/SubstitutionExamples.cs +++ b/src/DeepLTests/SubstitutionExamples.cs @@ -3,7 +3,8 @@ // license that can be found in the LICENSE file. using System.Collections.Generic; using System.Threading.Tasks; -using DeepL; +using DeepL.Model; +using DeepL.Model.Interfaces; using NSubstitute; using Xunit; diff --git a/src/DeepLTests/TranslateDocumentTest.cs b/src/DeepLTests/TranslateDocumentTest.cs index 12e1651..4c92eb4 100644 --- a/src/DeepLTests/TranslateDocumentTest.cs +++ b/src/DeepLTests/TranslateDocumentTest.cs @@ -11,6 +11,7 @@ using DeepL; using DeepL.Model; using DeepL.Model.Exceptions; +using DeepL.Model.Options; using Xunit; namespace DeepLTests { diff --git a/src/DeepLTests/TranslateTextTest.cs b/src/DeepLTests/TranslateTextTest.cs index 52ce417..ee7d1c5 100644 --- a/src/DeepLTests/TranslateTextTest.cs +++ b/src/DeepLTests/TranslateTextTest.cs @@ -9,6 +9,7 @@ using DeepL; using DeepL.Model; using DeepL.Model.Exceptions; +using DeepL.Model.Options; using Xunit; namespace DeepLTests {