diff --git a/Directory.Packages.props b/Directory.Packages.props
index 9040a4840b..4365925adc 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -30,6 +30,7 @@
+
diff --git a/docs/en/CLI.md b/docs/en/CLI.md
index 48858edc74..8c1b320861 100644
--- a/docs/en/CLI.md
+++ b/docs/en/CLI.md
@@ -527,6 +527,16 @@ Then review changes on your source control system to be sure that it has changed
* `--file` or `-f`: Default: `abp-translation.json`. The translation file (use only if you've used the `--output` option before).
+#### Online DeepL translate
+
+The `translate` command also supports online translation. You need to provide your [DeepL Authentication Key](https://support.deepl.com/hc/en-us/articles/360020695820-Authentication-Key).
+
+It will search all the `en.json(reference-culture)` files in the directory and sub-directory and then translate and generate the corresponding `zh-Hans.json(culture)` files.
+
+````bash
+abp translate -c zh-Hans --online --deepl-auth-key
+````
+
### login
Some features of the CLI requires to be logged in to abp.io platform. To login with your username write:
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj b/framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj
index 15f7406f76..589509bd22 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj
+++ b/framework/src/Volo.Abp.Cli.Core/Volo.Abp.Cli.Core.csproj
@@ -24,6 +24,7 @@
+
diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/TranslateCommand.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/TranslateCommand.cs
index ea6b1d45f6..73b6a74421 100644
--- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/TranslateCommand.cs
+++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/Commands/TranslateCommand.cs
@@ -5,6 +5,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using DeepL;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@@ -16,64 +17,61 @@ namespace Volo.Abp.Cli.Commands;
public class TranslateCommand : IConsoleCommand, ITransientDependency
{
public const string Name = "translate";
-
+
public ILogger Logger { get; set; }
- public Task ExecuteAsync(CommandLineArgs commandLineArgs)
+ public async Task ExecuteAsync(CommandLineArgs commandLineArgs)
{
var currentDirectory = Directory.GetCurrentDirectory();
+ var referenceCulture = commandLineArgs.Options.GetOrNull(Options.ReferenceCulture.Short, Options.ReferenceCulture.Long) ?? "en";
+ var allValues = commandLineArgs.Options.ContainsKey(Options.AllValues.Short) || commandLineArgs.Options.ContainsKey(Options.AllValues.Long);
- var apply = commandLineArgs.Options.ContainsKey(Options.Apply.Short) || commandLineArgs.Options.ContainsKey(Options.Apply.Long);
- if (apply)
+ // Apply abp-translation.json file
+ if (commandLineArgs.Options.ContainsKey(Options.Apply.Short) || commandLineArgs.Options.ContainsKey(Options.Apply.Long))
{
- var inputFile = Path.Combine(currentDirectory,
- commandLineArgs.Options.GetOrNull(Options.File.Short, Options.File.Long)
- ?? "abp-translation.json");
-
- Logger.LogInformation("Abp translate apply...");
- Logger.LogInformation("Input file: " + inputFile);
-
- ApplyAbpTranslateInfo(currentDirectory, inputFile);
+ var inputFile = Path.Combine(currentDirectory, commandLineArgs.Options.GetOrNull(Options.File.Short, Options.File.Long) ?? "abp-translation.json");
+ await ApplyAbpTranslateInfoAsync(currentDirectory, inputFile);
+ return;
}
- else
- {
- var targetCulture = commandLineArgs.Options.GetOrNull(Options.Culture.Short, Options.Culture.Long);
- if (targetCulture == null)
- {
- throw new CliUsageException(
- "Target culture is missing!" +
- Environment.NewLine + Environment.NewLine +
- GetUsageInfo()
- );
- }
-
- var referenceCulture = commandLineArgs.Options.GetOrNull(Options.ReferenceCulture.Short, Options.ReferenceCulture.Long)
- ?? "en";
-
- var outputFile = Path.Combine(currentDirectory,
- commandLineArgs.Options.GetOrNull(Options.Output.Short, Options.Output.Long)
- ?? "abp-translation.json");
- var allValues = commandLineArgs.Options.ContainsKey(Options.AllValues.Short) ||
- commandLineArgs.Options.ContainsKey(Options.AllValues.Long);
-
- Logger.LogInformation("Abp translate...");
- Logger.LogInformation("Target culture: " + targetCulture);
- Logger.LogInformation("Reference culture: " + referenceCulture);
- Logger.LogInformation("Output file: " + outputFile);
+ var targetCulture = commandLineArgs.Options.GetOrNull(Options.Culture.Short, Options.Culture.Long);
+ if (targetCulture == null)
+ {
+ throw new CliUsageException("Target culture is missing!" + Environment.NewLine + Environment.NewLine + GetUsageInfo());
+ }
- if (allValues)
+ // Translate online
+ if (commandLineArgs.Options.ContainsKey(Options.Online.Long))
+ {
+ var authKey = commandLineArgs.Options.GetOrNull(Options.DeepLAuthKey.Short, Options.DeepLAuthKey.Short);
+ if (authKey == null)
{
- Logger.LogInformation("Include all keys");
+ throw new CliUsageException("DeepL auth key is missing!" + Environment.NewLine + Environment.NewLine + GetUsageInfo());
}
+ await TranslateAbpTranslateInfoAsync(currentDirectory, targetCulture, referenceCulture, allValues, authKey);
+ return;
+ }
- var translateInfo = GetAbpTranslateInfo(currentDirectory, targetCulture, referenceCulture, allValues);
-
- File.WriteAllText(outputFile, JsonConvert.SerializeObject(translateInfo, Formatting.Indented));
+ // Generate abp-translation.json file
+ var outputFile = Path.Combine(currentDirectory, commandLineArgs.Options.GetOrNull(Options.Output.Short, Options.Output.Long) ?? "abp-translation.json");
+ await GenerateAbpTranslateInfoAsync(currentDirectory, targetCulture, referenceCulture, allValues, outputFile);
+ }
- Logger.LogInformation($"The translation file has been created.");
+ private Task GenerateAbpTranslateInfoAsync(string currentDirectory, string targetCulture, string referenceCulture, bool allValues, string outputFile)
+ {
+ Logger.LogInformation("Abp translate...");
+ Logger.LogInformation("Target culture: " + targetCulture);
+ Logger.LogInformation("Reference culture: " + referenceCulture);
+ Logger.LogInformation("Output file: " + outputFile);
+ if (allValues)
+ {
+ Logger.LogInformation("Include all keys");
}
+ var translateInfo = GetAbpTranslateInfo(currentDirectory, targetCulture, referenceCulture, allValues);
+ File.WriteAllText(outputFile, JsonConvert.SerializeObject(translateInfo, Formatting.Indented));
+ Logger.LogInformation($"The translation file has been created.");
+
return Task.CompletedTask;
}
@@ -140,8 +138,154 @@ public class TranslateCommand : IConsoleCommand, ITransientDependency
return translateInfo;
}
- private void ApplyAbpTranslateInfo(string directory, string filename)
+ private async Task TranslateAbpTranslateInfoAsync(string directory, string targetCulture, string referenceCulture, bool allValues, string authKey)
{
+ Logger.LogInformation("Abp translate online...");
+ Logger.LogInformation("Target culture: " + targetCulture);
+ Logger.LogInformation("Reference culture: " + referenceCulture);
+ if (allValues)
+ {
+ Logger.LogInformation("Include all keys");
+ }
+
+ var translateInfo = GetAbpTranslateInfo(directory, targetCulture, referenceCulture, allValues);
+ foreach (var resource in translateInfo.Resources)
+ {
+ var targetFile = Path.Combine(resource.ResourcePath, translateInfo.TargetCulture + ".json");
+ var targetLocalizationInfo = File.Exists(targetFile)
+ ? GetAbpLocalizationInfoOrNull(targetFile)
+ : new AbpLocalizationInfo()
+ {
+ Culture = translateInfo.TargetCulture,
+ Texts = new List()
+ };
+
+ if (targetLocalizationInfo == null)
+ {
+ throw new CliUsageException(
+ $"Failed to get localization information from {targetFile} file." +
+ Environment.NewLine + Environment.NewLine +
+ GetUsageInfo()
+ );
+ }
+
+ var referenceFile = Path.Combine(resource.ResourcePath, translateInfo.ReferenceCulture + ".json");
+ if (!File.Exists(referenceFile))
+ {
+ throw new CliUsageException(
+ $"{referenceFile} file does not exist.." +
+ Environment.NewLine + Environment.NewLine +
+ GetUsageInfo()
+ );
+ }
+ var referenceLocalizationInfo = GetAbpLocalizationInfoOrNull(referenceFile);
+ if (referenceLocalizationInfo == null)
+ {
+ throw new CliUsageException(
+ $"Failed to get localization information from {referenceFile} file." +
+ Environment.NewLine + Environment.NewLine +
+ GetUsageInfo()
+ );
+ }
+
+ var translator = new Translator(authKey);
+
+ var texts = resource.Texts.Select(x => x.Reference);
+
+ var translations = await translator.TranslateTextAsync(texts, await GetDeeplLanguageCode(referenceCulture), await GetDeeplLanguageCode(targetCulture));
+ for (var i = 0; i < translations.Length; i++)
+ {
+ resource.Texts[i].Target = translations[i].Text;
+ }
+
+ foreach (var text in resource.Texts)
+ {
+ var targetText = targetLocalizationInfo.Texts.FirstOrDefault(x => x.Name == text.LocalizationKey);
+ if (targetText != null)
+ {
+ if (!text.Target.IsNullOrEmpty())
+ {
+ Logger.LogInformation($"Update translation: {targetText.Name} => " + text.Target);
+ targetText.Value = text.Target;
+ }
+ }
+ else
+ {
+ Logger.LogInformation($"Create translation: {text.LocalizationKey} => " + text.Target);
+ targetLocalizationInfo.Texts.Add(new NameValue(text.LocalizationKey, text.Target));
+ }
+ }
+
+ Logger.LogInformation($"Write translation json to {targetFile}.");
+
+ // sort keys
+ targetLocalizationInfo = SortLocalizedKeys(targetLocalizationInfo, referenceLocalizationInfo);
+ File.WriteAllText(targetFile, AbpLocalizationInfoToJsonFile(targetLocalizationInfo));
+ }
+ }
+
+ private Task GetDeeplLanguageCode(string abpCulture)
+ {
+ var deeplLanguages = new List()
+ {
+ LanguageCode.Bulgarian ,
+ LanguageCode.Czech ,
+ LanguageCode.Danish ,
+ LanguageCode.German ,
+ LanguageCode.Greek ,
+ LanguageCode.English ,
+ LanguageCode.EnglishBritish ,
+ LanguageCode.EnglishAmerican ,
+ LanguageCode.Spanish ,
+ LanguageCode.Estonian ,
+ LanguageCode.Finnish ,
+ LanguageCode.French ,
+ LanguageCode.Hungarian ,
+ LanguageCode.Indonesian ,
+ LanguageCode.Italian ,
+ LanguageCode.Japanese ,
+ LanguageCode.Korean ,
+ LanguageCode.Lithuanian ,
+ LanguageCode.Latvian ,
+ LanguageCode.Norwegian ,
+ LanguageCode.Dutch ,
+ LanguageCode.Polish ,
+ LanguageCode.Portuguese ,
+ LanguageCode.PortugueseBrazilian ,
+ LanguageCode.PortugueseEuropean ,
+ LanguageCode.Romanian ,
+ LanguageCode.Russian ,
+ LanguageCode.Slovak ,
+ LanguageCode.Slovenian ,
+ LanguageCode.Swedish ,
+ LanguageCode.Turkish ,
+ LanguageCode.Ukrainian,
+ LanguageCode.Chinese
+ };
+
+ if (abpCulture == "zh-Hans")
+ {
+ return Task.FromResult(LanguageCode.Chinese);
+ }
+
+ var deeplCulture = deeplLanguages.FirstOrDefault(x => x.Equals(abpCulture, StringComparison.OrdinalIgnoreCase));
+ if (deeplCulture == null)
+ {
+ throw new CliUsageException(
+ $"DeepL does not support {abpCulture} culture." +
+ Environment.NewLine + Environment.NewLine +
+ GetUsageInfo()
+ );
+ }
+
+ return Task.FromResult(deeplCulture);
+ }
+
+ private Task ApplyAbpTranslateInfoAsync(string directory, string filename)
+ {
+ Logger.LogInformation("Abp translate apply...");
+ Logger.LogInformation("Input file: " + filename);
+
var translateJsonPath = Path.Combine(directory, filename);
if (!File.Exists(translateJsonPath))
{
@@ -220,6 +364,8 @@ public class TranslateCommand : IConsoleCommand, ITransientDependency
File.Delete(translateJsonPath);
Logger.LogInformation($"Delete the {translateJsonPath} file, if you need to translate again, please re-run the [abp translate] command.");
}
+
+ return Task.CompletedTask;
}
private static IEnumerable GetCultureJsonFiles(string path, string cultureName)
@@ -357,13 +503,17 @@ public class TranslateCommand : IConsoleCommand, ITransientDependency
sb.AppendLine("--all-values|-all Include all keys. Default false");
sb.AppendLine("--apply|-a Creates or updates the file for the translated culture.");
sb.AppendLine("--file|-f Default: abp-translation.json");
+ sb.AppendLine("--online|-o Translate online.");
+ sb.AppendLine("--deepl-auth-key DeepL auth key for online translation.");
sb.AppendLine("");
sb.AppendLine("Examples:");
sb.AppendLine("");
sb.AppendLine(" abp translate -c zh-Hans");
- sb.AppendLine(" abp translate -c zh-Hans -r en -a");
+ sb.AppendLine(" abp translate -c zh-Hans -r en");
sb.AppendLine(" abp translate --apply");
sb.AppendLine(" abp translate -a -f my-translation.json");
+ sb.AppendLine(" abp translate -c zh-Hans --online --deepl-auth-key ");
+ sb.AppendLine(" abp translate -c zh-Hans -r tr --online --deepl-auth-key ");
sb.AppendLine("");
sb.AppendLine("See the documentation for more info: https://docs.abp.io/en/abp/latest/CLI");
@@ -412,6 +562,16 @@ public class TranslateCommand : IConsoleCommand, ITransientDependency
public const string Short = "f";
public const string Long = "file";
}
+
+ public static class Online
+ {
+ public const string Long = "online";
+ }
+
+ public static class DeepLAuthKey
+ {
+ public const string Short = "deepl-auth-key";
+ }
}
public class AbpTranslateInfo