Browse Source

Merge pull request #15779 from abpframework/salihozkara/localization-key-synchronizer

Add localization-key-synchronizer tool
pull/15851/head
Alper Ebiçoğlu 3 years ago
committed by GitHub
parent
commit
2318705cba
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 44
      docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/POST.md
  2. BIN
      docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/images/Part1.gif
  3. BIN
      docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/images/Part2.gif
  4. BIN
      docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/images/Part3.gif
  5. BIN
      tools/localization-key-synchronizer/LocalizationKeySynchronizer.exe
  6. 16
      tools/localization-key-synchronizer/LocalizationKeySynchronizer.sln
  7. 27
      tools/localization-key-synchronizer/README.md
  8. 16
      tools/localization-key-synchronizer/src/AbpAsyncKey.cs
  9. 18
      tools/localization-key-synchronizer/src/AbpAsyncLocalization.cs
  10. 20
      tools/localization-key-synchronizer/src/AbpAsyncLocalizationViewModel.cs
  11. 14
      tools/localization-key-synchronizer/src/AbpLocalization.cs
  12. 21
      tools/localization-key-synchronizer/src/AbpLocalizationInfo.cs
  13. 15
      tools/localization-key-synchronizer/src/ArgumentCountMismatch.cs
  14. 26
      tools/localization-key-synchronizer/src/JsonHelper.cs
  15. 173
      tools/localization-key-synchronizer/src/LocalizationHelper.cs
  16. 15
      tools/localization-key-synchronizer/src/LocalizationKeySynchronizer.csproj
  17. 8
      tools/localization-key-synchronizer/src/MissingKey.cs
  18. 173
      tools/localization-key-synchronizer/src/Program.cs
  19. 57
      tools/localization-key-synchronizer/src/Questions.cs

44
docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/POST.md

@ -0,0 +1,44 @@
## Streamline Localization in Your ABP Project
Making localization changes to an ABP project can be a daunting task, especially if you're dealing with multiple languages and translations. During development, it's easy to overlook some changes and that leads to inconsistencies across different languages. Fortunately, I have developed a tool that can help streamline the localization process and ensure consistency across different languages.
The tool is a console application that uses JSON files to manage localization keys and their translations. It addresses three common scenarios that can arise during localization:
1. When the argument count of a key changes, it can be difficult to update the translations for all languages. My tool solves this problem by scanning all JSON files in the project folder and identifying any keys that have mismatched argument counts. It then offers two options to the user: delete the mismatched translations or export them as a JSON file for manual editing.
2. When a new key is added to the project, forgetting to add its translations to all the other languages is easy. My tool helps to avoid this issue by scanning the default language's JSON file and identifying any keys that don't have translations in other languages. It then exports these keys as a JSON file that can be used to add missing translations.
3. When a key's name is changed, it's important to update its translations in all the other languages. My tool makes this task simple by scanning all the JSON files in the project folder and updating any translations of the old key name with the new one.
The tool also includes an export feature that allows users to modify translations outside of the application and import them back into the JSON files.
## How it Helps
With my Localization Key Synchronizer tool, you can perform complex localization changes more quickly and easily than by manually sifting through files and making changes one-by-one. This can save you significant time and effort, especially if you're working with a large number of languages or translations.
## How it Works
When you run the Localization Key Synchronizer tool, it presents you with three options:
1. Find Asynchronous Keys
2. Apply Changes in the Exported File
3. Replace Keys
If you select "Find Asynchronous Keys," the tool prompts you to enter the default language path. Once you've entered the path, the tool displays all of the JSON files in the same folder as a multi-select list. After selecting one or more files, you are asked whether you want to find keys that do not match the number of arguments, missing keys, or both. If you select "Missing Keys," the tool prompts you to enter the absolute path to export the missing keys. After you've entered the path, the export process starts, and the tool closes.
![](./images/Part1.gif)
If you select "Apply Changes in the Exported File" at the main menu, the tool prompts you to enter the path to the exported file. After you've entered the path, the import process starts, and the tool closes.
![](./images/Part2.gif)
If you select "Replace Keys," the tool prompts you to enter the localization folder path, the old key, the new key, and the JSON files to apply the changes to. Once you've entered all the required information and made your selections, the tool performs the replacements and closes.
![](./images/Part3.gif)
## Conclusion
If you're struggling to manage localization changes in an ABP project, give my Localization Key Synchronizer tool a try. It can help streamline your workflow and make the process much more manageable. You can find the tool on [GitHub](https://github.com/abpframework/abp/tree/dev/tools/localization-key-synchronizer).
To use the tool, simply run the console application and follow the prompts. It's a user-friendly solution that helps to ensure localization consistency in your ABP project. Give it a try and let me know what you think!

BIN
docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/images/Part1.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

BIN
docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/images/Part2.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

BIN
docs/en/Community-Articles/2023-02-22-Streamline-Localization-In-Your-ABP-Project/images/Part3.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

BIN
tools/localization-key-synchronizer/LocalizationKeySynchronizer.exe

Binary file not shown.

16
tools/localization-key-synchronizer/LocalizationKeySynchronizer.sln

@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LocalizationKeySynchronizer", "src\LocalizationKeySynchronizer.csproj", "{FFD8DF5E-2724-4D15-9C84-7ACFEEF6F270}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FFD8DF5E-2724-4D15-9C84-7ACFEEF6F270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FFD8DF5E-2724-4D15-9C84-7ACFEEF6F270}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FFD8DF5E-2724-4D15-9C84-7ACFEEF6F270}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FFD8DF5E-2724-4D15-9C84-7ACFEEF6F270}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

27
tools/localization-key-synchronizer/README.md

@ -0,0 +1,27 @@
# Streamline Localization in Your ABP Project with this Console Tool
Are you tired of manually managing your localization files for your software projects? Look no further than our new console application designed to streamline your localization workflow!
![image](https://user-images.githubusercontent.com/58659931/218817197-827d1934-4378-4ccb-87d3-a9118cb5203d.png)
The application's main menu provides three options: find asynchronous keys, apply changes in the exported file, or replace keys. Let's take a look at each option in more details.
![image](https://user-images.githubusercontent.com/58659931/218817488-0ba34e67-3039-4162-8291-5eb4669a8868.png)
![image](https://user-images.githubusercontent.com/58659931/218818263-d8d2d8c5-fc77-40a6-ba5c-e1fb7a0180be.png)
![image](https://user-images.githubusercontent.com/58659931/218818359-79a65d48-4895-4ed8-9d34-b6282939ca48.png)
If you choose to find asynchronous keys, you will be prompted to enter the default language path. Once entered, a multi-select menu will appear with all the JSON files in that folder. You can choose to find keys that do not match the number of arguments, missing keys, or both. If you choose to find the missing keys, you will be prompted to enter the absolute path to export the missing keys. Once entered, the export process will begin, and the application will close.
![image](https://user-images.githubusercontent.com/58659931/218818963-747766a5-b1c0-420f-95d2-7017a5f63925.png)
If you choose to find the keys that do not match the number of arguments, you can choose to delete, export, or both. If you choose to delete, the process will complete, and the application will close. If you choose to export, you will be prompted to enter the export path. Once entered, the export process will begin, and the application will close. If you choose to do both, both processes will complete, and the application will close.
![image](https://user-images.githubusercontent.com/58659931/218820012-e1596f88-5916-4f4f-a482-402c692ed207.png)
If you choose to apply changes in the exported file, you will be prompted to import the file, and the process will complete.
![image](https://user-images.githubusercontent.com/58659931/218820350-bfc0ccae-06e8-46d6-853d-7a2d1df3b156.png)
If you choose to replace keys, you will be prompted to enter the localization folder path, the old key, the new key, and select the JSON files where you want the changes to be made. Once all information is entered, the application will begin the replacement process, and it will close.
Overall, our console application offers a simple and effective solution to manage your localization files. Try it out yourself today!

16
tools/localization-key-synchronizer/src/AbpAsyncKey.cs

@ -0,0 +1,16 @@
namespace LocalizationKeySynchronizer;
public class AbpAsyncKey
{
public string NewValue = string.Empty;
public AbpAsyncKey(string key, string reference)
{
Key = key;
Reference = reference;
}
public virtual string Type => GetType().Name;
public string Key { get; set; }
public string Reference { get; set; }
}

18
tools/localization-key-synchronizer/src/AbpAsyncLocalization.cs

@ -0,0 +1,18 @@
using System.Collections.Generic;
namespace LocalizationKeySynchronizer;
public class AbpAsyncLocalization
{
public AbpAsyncLocalization(AbpLocalization localization, AbpLocalization reference, List<AbpAsyncKey> asyncKeys)
{
Localization = localization;
Reference = reference;
AsyncKeys = asyncKeys;
}
public AbpLocalization Localization { get; set; }
public AbpLocalization Reference { get; set; }
public List<AbpAsyncKey> AsyncKeys { get; set; }
}

20
tools/localization-key-synchronizer/src/AbpAsyncLocalizationViewModel.cs

@ -0,0 +1,20 @@
using System.Collections.Generic;
namespace LocalizationKeySynchronizer;
public class AbpAsyncLocalizationViewModel
{
public AbpAsyncLocalizationViewModel(string referenceCulture, string culture, string path, List<AbpAsyncKey> asyncKeys)
{
ReferenceCulture = referenceCulture;
Culture = culture;
Path = path;
AsyncKeys = asyncKeys;
}
public string ReferenceCulture { get; set; }
public string Culture { get; set; }
public string Path { get; set; }
public List<AbpAsyncKey> AsyncKeys { get; set; }
}

14
tools/localization-key-synchronizer/src/AbpLocalization.cs

@ -0,0 +1,14 @@
namespace LocalizationKeySynchronizer;
public class AbpLocalization
{
public AbpLocalization(string filePath, AbpLocalizationInfo localizationInfo)
{
FilePath = filePath;
LocalizationInfo = localizationInfo;
}
public string FilePath { get; set; }
public AbpLocalizationInfo LocalizationInfo { get; set; }
}

21
tools/localization-key-synchronizer/src/AbpLocalizationInfo.cs

@ -0,0 +1,21 @@
using System.Collections.Generic;
namespace LocalizationKeySynchronizer;
// This class is used to deserialize the JSON string from culture file.
public class AbpLocalizationInfo
{
public AbpLocalizationInfo(string culture, Dictionary<string, string> texts)
{
Culture = culture;
Texts = texts;
}
public string Culture { get; set; }
public Dictionary<string, string> Texts { get; set; }
public static bool TryDeserialize(string json, out AbpLocalizationInfo? localizationInfo)
{
return JsonHelper.TryDeserialize(json, out localizationInfo);
}
}

15
tools/localization-key-synchronizer/src/ArgumentCountMismatch.cs

@ -0,0 +1,15 @@
namespace LocalizationKeySynchronizer;
public class ArgumentCountMismatch : AbpAsyncKey
{
public ArgumentCountMismatch(string key, string reference, int referenceArgumentCount, int argumentCount, string value) : base(key, reference)
{
ReferenceArgumentCount = referenceArgumentCount;
ArgumentCount = argumentCount;
Value = value;
}
public int ReferenceArgumentCount { get; }
public int ArgumentCount { get; }
public string Value { get; }
}

26
tools/localization-key-synchronizer/src/JsonHelper.cs

@ -0,0 +1,26 @@
using System;
using Newtonsoft.Json;
namespace LocalizationKeySynchronizer;
public static class JsonHelper
{
public static bool TryDeserialize<T>(string json, out T? result)
{
try
{
result = JsonConvert.DeserializeObject<T>(json);
return true;
}
catch (Exception)
{
result = default;
return false;
}
}
public static string Serialize<T>(T value)
{
return JsonConvert.SerializeObject(value, Formatting.Indented);
}
}

173
tools/localization-key-synchronizer/src/LocalizationHelper.cs

@ -0,0 +1,173 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace LocalizationKeySynchronizer;
public static partial class LocalizationHelper
{
public static bool TryGetLocalization(string path, out AbpLocalizationInfo? localizationInfo)
{
if (File.Exists(path) == false)
{
localizationInfo = default;
return false;
}
var json = File.ReadAllTextAsync(path).GetAwaiter().GetResult();
return AbpLocalizationInfo.TryDeserialize(json, out localizationInfo);
}
public static List<AbpLocalization> GetLocalizations(IEnumerable<string> paths)
{
var results = new List<AbpLocalization>();
foreach (var path in paths)
{
if (TryGetLocalization(path, out var localizationInfo))
{
results.Add(new AbpLocalization(path, localizationInfo!));
}
}
return results;
}
private static Dictionary<string, int> GetKeysAndArgCount(this AbpLocalizationInfo localizationInfo)
{
return localizationInfo.Texts.ToDictionary(k => k.Key, v => GetArgCount(v.Value));
}
private static int GetArgCount(string value)
{
var matches = MyRegex().Matches(value);
return matches.Count;
}
public static List<AbpAsyncLocalization> GetAsynchronousLocalizations(this AbpLocalization defaultLocalization,
IEnumerable<AbpLocalization> otherLocalizations)
{
var results = new List<AbpAsyncLocalization>();
var defaultCultureKeysAndArgCount = defaultLocalization.LocalizationInfo.GetKeysAndArgCount();
foreach (var localization in otherLocalizations)
{
var keysAndArgCount = localization.LocalizationInfo.GetKeysAndArgCount();
var asynchronousResource =
new AbpAsyncLocalization(localization, defaultLocalization, new List<AbpAsyncKey>());
foreach (var (key, defaultCultureArgCount) in defaultCultureKeysAndArgCount)
{
if (keysAndArgCount.TryGetValue(key, out var value))
{
if (value != defaultCultureArgCount)
{
asynchronousResource.AsyncKeys.Add(new ArgumentCountMismatch(key,
defaultLocalization.LocalizationInfo.Texts[key], defaultCultureArgCount, value,
localization.LocalizationInfo.Texts[key]));
}
}
else
{
asynchronousResource.AsyncKeys.Add(new MissingKey(key,
defaultLocalization.LocalizationInfo.Texts[key]));
}
}
if (asynchronousResource.AsyncKeys.Any())
{
results.Add(asynchronousResource);
}
}
return results;
}
public static void DeleteKeysThatDoNotMatchTheNumberOfArguments(
IEnumerable<AbpAsyncLocalization> asynchronousResources)
{
foreach (var resource in asynchronousResources)
{
foreach (var key in resource.AsyncKeys.Select(x => x.Key))
{
resource.Localization.LocalizationInfo.Texts.Remove(key);
}
File.WriteAllTextAsync(resource.Localization.FilePath,
JsonHelper.Serialize(resource.Localization.LocalizationInfo)).GetAwaiter().GetResult();
}
}
public static void ExportKeysThatDoNotMatchTheNumberOfArguments(
IEnumerable<AbpAsyncLocalization> asynchronousResources, string? exportPath)
{
Export<ArgumentCountMismatch>(asynchronousResources, exportPath);
}
public static void Export<T>(IEnumerable<AbpAsyncLocalization> asynchronousResources, string? exportPath)
where T : AbpAsyncKey
{
var asyncLocalizationViewModels = asynchronousResources.Select(x =>
new AbpAsyncLocalizationViewModel(x.Reference.LocalizationInfo.Culture,
x.Localization.LocalizationInfo.Culture, x.Localization.FilePath,
x.AsyncKeys.Where(k => k is T).ToList())).ToList();
if (exportPath != null)
{
File.WriteAllTextAsync(exportPath,
JsonHelper.Serialize(asyncLocalizationViewModels))
.GetAwaiter().GetResult();
}
}
public static void ExportMissingKeys(IEnumerable<AbpAsyncLocalization> asyncLocalizations, string? exportPath)
{
Export<MissingKey>(asyncLocalizations, exportPath);
}
public static bool ApplyChanges(string path)
{
var json = File.ReadAllTextAsync(path).GetAwaiter().GetResult();
if (JsonHelper.TryDeserialize(json, out List<AbpAsyncLocalizationViewModel>? asyncLocalizationViewModels) ==
false)
{
return false;
}
foreach (var asyncLocalizationViewModel in asyncLocalizationViewModels!)
{
if (TryGetLocalization(asyncLocalizationViewModel.Path, out var localizationInfo) == false)
{
return false;
}
foreach (var asyncKey in asyncLocalizationViewModel.AsyncKeys.Where(asyncKey =>
!string.IsNullOrWhiteSpace(asyncKey.NewValue)))
{
localizationInfo!.Texts[asyncKey.Key] = asyncKey.NewValue;
}
File.WriteAllTextAsync(asyncLocalizationViewModel.Path,
JsonHelper.Serialize(localizationInfo)).GetAwaiter().GetResult();
}
return true;
}
public static void ReplaceKey(string oldKey, string newKey, List<AbpLocalization> localizations)
{
foreach (var localization in localizations)
{
if (!localization.LocalizationInfo.Texts.TryGetValue(oldKey, out var value))
{
continue;
}
localization.LocalizationInfo.Texts.Remove(oldKey);
localization.LocalizationInfo.Texts.Add(newKey, value);
File.WriteAllTextAsync(localization.FilePath,
JsonHelper.Serialize(localization.LocalizationInfo)).GetAwaiter().GetResult();
}
}
[GeneratedRegex("{(\\d+)}")]
private static partial Regex MyRegex();
}

15
tools/localization-key-synchronizer/src/LocalizationKeySynchronizer.csproj

@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3-beta1" />
<PackageReference Include="Spectre.Console" Version="0.46.1-preview.0.7" />
</ItemGroup>
</Project>

8
tools/localization-key-synchronizer/src/MissingKey.cs

@ -0,0 +1,8 @@
namespace LocalizationKeySynchronizer;
public class MissingKey : AbpAsyncKey
{
public MissingKey(string key, string reference) : base(key, reference)
{
}
}

173
tools/localization-key-synchronizer/src/Program.cs

@ -0,0 +1,173 @@
using System;
using System.IO;
using System.Linq;
using LocalizationKeySynchronizer;
using Spectre.Console;
using static LocalizationKeySynchronizer.Questions;
try
{
// Do you want to find asynchronous keys, apply changes in the exported file or replace the keys?
var option = AnsiConsole.Prompt(
new SelectionPrompt<string>()
.Title(_1.Question)
.AddChoices(_1.Options.Find, _1.Options.Apply, _1.Options.Replace));
switch (option)
{
case _1.Options.Apply:
{
// Enter the absolute path to the exported file:
var path = AnsiConsole.Ask<string>(_2);
if (!File.Exists(path))
{
AnsiConsole.MarkupLine("[red]The file does not exist![/]");
Exit();
}
if (LocalizationHelper.ApplyChanges(path))
{
AnsiConsole.MarkupLine("[green]The changes have been applied successfully![/]");
Exit();
}
AnsiConsole.MarkupLine("[red]An error occurred while applying changes![/]");
Exit();
break;
}
case _1.Options.Find:
{
// The default language path
var path = AnsiConsole.Ask<string>(_3);
if (!LocalizationHelper.TryGetLocalization(path, out var defaultLocalizationInfo))
{
AnsiConsole.MarkupLine("[red]The default language path is invalid![/]");
Exit();
}
var defaultCulture = new AbpLocalization(path, defaultLocalizationInfo!);
// Get others cultures
var paths = Directory.GetFiles(Path.GetDirectoryName(path)!, "*.json",
SearchOption.TopDirectoryOnly);
var otherCulturePaths = paths.Select(Path.GetFileNameWithoutExtension).Where(x=>!string.IsNullOrWhiteSpace(x)).Select(x=>x!).ToList();
// select other cultures
paths = AnsiConsole.Prompt(
new MultiSelectionPrompt<string>()
.Title("Select other cultures")
.PageSize(10).AddChoiceGroup("All", otherCulturePaths))
.Select(x => Path.Combine(Path.GetDirectoryName(path)!, x + ".json"))
.ToArray();
var otherCultures = LocalizationHelper.GetLocalizations(paths);
var asyncLocalizations = defaultCulture.GetAsynchronousLocalizations(otherCultures);
// Find keys that do not match the number of arguments, find missing keys, or both
var options = AnsiConsole.Prompt(
new MultiSelectionPrompt<string>()
.Title(_4.Question)
.PageSize(10)
.AddChoiceGroup("All", _4.Options.ArgumentsCount, _4.Options.MissingKeys));
// For arguments
// Find keys that do not match the number of arguments
string? exportPath = null;
if (options.Contains(_4.Options.ArgumentsCount))
{
// Should the keys that do not match the number of arguments be deleted, exported or both?
var options2 = AnsiConsole.Prompt(
new MultiSelectionPrompt<string>()
.Title(_5.Question)
.PageSize(10)
.AddChoiceGroup("All", _5.Options.Delete, _5.Options.Export));
// Delete the keys that do not match the number of arguments
if (options2.Contains(_5.Options.Delete))
{
LocalizationHelper.DeleteKeysThatDoNotMatchTheNumberOfArguments(asyncLocalizations);
}
// Ask for the export path and export it
if (options2.Contains(_5.Options.Export))
{
if (options.Contains(_4.Options.MissingKeys))
{
exportPath = AnsiConsole.Ask<string>(_8);
LocalizationHelper.Export<AbpAsyncKey>(asyncLocalizations, exportPath);
}
else
{
exportPath = AnsiConsole.Ask<string>(_6);
LocalizationHelper.ExportKeysThatDoNotMatchTheNumberOfArguments(asyncLocalizations, exportPath);
}
}
}
// For missing keys
// Export missing keys
if (options.Contains(_4.Options.MissingKeys))
{
if (string.IsNullOrEmpty(exportPath))
{
exportPath = AnsiConsole.Ask<string>(_7);
LocalizationHelper.ExportMissingKeys(asyncLocalizations, exportPath);
}
}
break;
}
case _1.Options.Replace:
{
// The localization folder path
var path = AnsiConsole.Ask<string>(_9);
// Old key
var oldKey = AnsiConsole.Ask<string>(_10);
// New key
var newKey = AnsiConsole.Ask<string>(_11);
// Localization paths
var paths = Directory.GetFiles(path, "*.json",
SearchOption.TopDirectoryOnly);
// Select localizations
var localizationPaths = paths.Select(Path.GetFileNameWithoutExtension).Where(x=>!string.IsNullOrWhiteSpace(x)).Select(x=>x!).ToList();
paths = AnsiConsole.Prompt(
new MultiSelectionPrompt<string>()
.Title("Select localizations")
.PageSize(10)
.AddChoiceGroup("All", localizationPaths))
.Select(x => Path.Combine(path, x + ".json"))
.ToArray();
var cultures = LocalizationHelper.GetLocalizations(paths);
// Replace keys
LocalizationHelper.ReplaceKey(oldKey, newKey, cultures);
AnsiConsole.MarkupLine("[green]The keys have been replaced successfully![/]");
break;
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
AnsiConsole.MarkupLine($"[red]{e.Message}[/]");
Exit();
}
void Exit()
{
AnsiConsole.MarkupLine("[red]Press any key to exit...[/]");
Console.ReadKey();
Environment.Exit(0);
}

57
tools/localization-key-synchronizer/src/Questions.cs

@ -0,0 +1,57 @@
namespace LocalizationKeySynchronizer;
public static class Questions
{
public const string _2 = "Enter the absolute path to the exported file:";
public const string _3 = "Enter the default language path:";
public const string _6 = "Enter the absolute path to export the keys that do not match the number of arguments:";
public const string _7 = "Enter the absolute path to export the missing keys:";
public const string _8 = "Enter the export path:";
public const string _9 = "Enter the localization folder path:";
public const string _10 = "Enter the old key:";
public const string _11 = "Enter the new key:";
public static class _1
{
public const string Question =
"Do you want to find asynchronous keys, apply changes in the exported file or replace the keys?";
public static class Options
{
public const string Find = "Find asynchronous keys";
public const string Apply = "Apply changes in the exported file";
public const string Replace = "Replace keys";
}
}
public static class _4
{
public const string Question =
"Find keys that do not match the number of arguments, find missing keys, or both?";
public static class Options
{
public const string ArgumentsCount = "Not matching arguments count";
public const string MissingKeys = "Missing keys";
}
}
public static class _5
{
public const string Question =
"Should the keys that do not match the number of arguments be deleted, exported or both?";
public static class Options
{
public const string Delete = "Delete";
public const string Export = "Export";
}
}
}
Loading…
Cancel
Save