Recentemente, tive o problema de localizar meu aplicativo e pensei em resolvê-lo.
A primeira que vem à mente é a maneira mais óbvia e simples - um dicionário, mas foi imediatamente rejeitado, uma vez que não há como verificar se uma string existe no dicionário no momento da compilação.
Uma solução muito mais elegante é criar uma hierarquia de classes como esta:
public class Locale
{
public string Name {get; set;}
public UI UI {get; set;}
}
public class UI
{
public Buttons Buttons {get; set;}
public Messages Messages {get; set;}
}
public class Buttons
{
public string CloseButton {get; set;}
public string DeleteButton {get; set;}
}
public class Messages
{
public string ErrorMessage {get; set;}
}
Em seguida, você pode simplesmente serializar / desserializar xml'ku.
Existe apenas um "mas". Pode levar muito tempo para criar essa hierarquia de classes, especialmente se o projeto for grande. Então, por que não gerá-lo a partir de um arquivo xml? Isso é o que faremos.
Vamos começar
Primeiro, vamos criar um projeto para nosso gerador e adicionar os pacotes necessários a ele.
dotnet new classlib -o LocalizationSourceGenerator -f netstandard2.0 dotnet add package Microsoft.CodeAnalysis.CSharp dotnet add package Microsoft.CodeAnalysis.Analyzers
Importante! A estrutura de destino do projeto deve ser netstandard2.0
A seguir, vamos adicionar a classe do nosso gerador
Deve implementar a interface ISourceGenerator e ser marcado com o atributo Generator
A seguir, vamos adicionar a interface ILocalizationGenerator e a classe XmlLocalizationGenerator que a implementa:
ILocalizationGenerator.cs
public interface ILocalizationGenerator
{
string GenerateLocalization(string template);
}
XmlLocalizationGenerator.cs
public class XmlLocalizationGenerator : ILocalizationGenerator
{
//
private List<string> classes = new List<string>();
public string GenerateLocalization(string template)
{
// xml
XmlDocument document = new XmlDocument();
document.LoadXml(template);
var root = document.DocumentElement;
//
string namespaceName = root.HasAttribute("namespace") ?
root.GetAttribute("namespace") :
"Localization";
GenClass(root); //
var sb = new StringBuilder();
sb.AppendLine($"namespace {namespaceName}\n{{");
//
foreach(var item in classes)
{
sb.AppendLine(item);
}
sb.Append('}');
return sb.ToString();
}
public void GenClass(XmlElement element)
{
var sb = new StringBuilder();
sb.Append($"public class {element.Name}");
sb.AppendLine("{");
//
foreach (XmlNode item in element.ChildNodes)
{
//
// - -
if (item.ChildNodes.Count == 0
|| (item.ChildNodes.Count == 1
&& item.FirstChild.NodeType==XmlNodeType.Text))
{
sb.AppendLine($"public string {item.Name} {{get; set;}}");
}
else
{
//
//
sb.AppendLine($"public {item.Name} {item.Name} {{get; set;}}");
GenClass(item);
}
}
sb.AppendLine("}");
classes.Add(sb.ToString());
}
}
Pouco resta a fazer. É necessário implementar a classe do próprio gerador
LocalizationSourceGenerator.cs
[Generator]
public class LocalizationSourceGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
//
var templateFile = context
.AdditionalFiles
.FirstOrDefault(
x => Path.GetExtension(x.Path) == ".xml")
?.Path;
if (!string.IsNullOrWhiteSpace(templateFile))
{
ILocalizationGenerator generator = new XmlLocalizationGenerator();
var s = generator.GenerateLocalization(File.ReadAllText(templateFile));
// ""
//
context.AddSource("Localization",s );
}
}
public void Initialize(GeneratorInitializationContext context)
{
// ,
//
}
}
Isso é tudo! Agora você só precisa verificar nosso gerador. Para fazer isso, crie um projeto de aplicativo de console
dotnet new console -o Test
Adicione o modelo e o arquivo de localização
template.xml
<Locale namespace="Program.Localization">
<UI>
<Buttons>
<SendButton/>
</Buttons>
</UI>
<Name/>
</Locale>
ru.xml
<Locale>
<UI>
<Buttons>
<SendButton></SendButton>
</Buttons>
</UI>
<Name></Name>
</Locale>
Vamos editar o arquivo do projeto
Test.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference
ReferenceOutputAssembly="false"
OutputItemType="Analyzer"
Include="----" />
<!-- -->
<AdditionalFiles Include="template.xml"/>
</ItemGroup>
</Project>
E o código do programa
Program.cs
using System;
using System.IO;
using System.Xml.Serialization;
using Program.Localization; //
namespace Program
{
public class Program
{
public static void Main()
{
// Locale
var xs = new XmlSerializer(typeof(Locale));
var locale = xs.Deserialize(File.OpenRead("ru.xml")) as Locale;
Console.WriteLine(locale.Name);
Console.WriteLine(locale.UI.Buttons.SendButton);
}
}
}
Dotnet-And-Happiness / LocalizationSourceGenerator (github.com) - repositório gerador