> ## Content Index
> Fetch the complete content index at: https://www.azurebrasil.cloud/llms.txt
> Use this file to discover other available public pages before exploring further.

# Como criar localizations de forma automática com o Azure AI Translator
- URL: https://www.azurebrasil.cloud/blog/como-criar-localizations-de-forma-automatica-com-o-azure-translator-2/
- Published: 2024-09-27T14:16:43.000Z
- Updated: 2024-09-27T14:16:43.000Z
- Author: Talles Valiatti

Aplicações modernas precisam suportar [localizações](https://learn.microsoft.com/en-us/dotnet/core/extensions/localization?ref=azurebrasil.cloud) para garantir que possam atender a uma audiência global. A localização permite que os usuários interajam com o aplicativo em seu idioma nativo, ajustando também formatos de data, moeda e outras preferências regionais. Isso melhora a experiência do usuário e a acessibilidade do software, aumentando o alcance e a aceitação da aplicação em diferentes mercados.

No C#, a [localização](https://learn.microsoft.com/en-us/dotnet/core/extensions/localization?ref=azurebrasil.cloud) é facilitada através do uso de [arquivos **.resx** (*resource File*)](https://learn.microsoft.com/en-us/dotnet/core/extensions/localization?ref=azurebrasil.cloud#resource-files), que armazenam strings e outros dados localizáveis. Esses arquivos permitem que o conteúdo da aplicação seja traduzido sem modificar o código-fonte, garantindo flexibilidade e escalabilidade. Cada idioma ou região pode ter seu próprio arquivo **.resx**, e o C# seleciona automaticamente o recurso apropriado com base na cultura definida pelo usuário.

Você pode aprender mais sobre localizações no C# e o uso de arquivos **.resx** na [documentação oficial](https://learn.microsoft.com/en-us/dotnet/core/extensions/localization?ref=azurebrasil.cloud).

Para começar o projeto, vamos criar um diretório raiz para o projeto, iniciar o repositório Git, configurar o [**gitignore**](https://git-scm.com/docs/gitignore?ref=azurebrasil.cloud) e criar uma nova [solução .NET](https://learn.microsoft.com/en-au/visualstudio/ide/solutions-and-projects-in-visual-studio?view=vs-2022&ref=azurebrasil.cloud):

```bash
mkdir CreateLocalizationsAutomatically
git init
dotnet new gitignore
dotnet new sln

```

Para criar o primeiro projeto da solução, vamos criar uma [*class Library no .NET*](https://learn.microsoft.com/en-au/dotnet/standard/class-libraries?ref=azurebrasil.cloud). Esse projeto será responsável por conter as localizações compartilhadas que poderão ser usadas por outros projetos da solução. Esse tipo de projeto é útil para manter os recursos localizáveis centralizados e organizados.

Certifique-se de estar no diretório raiz da solução criada anteriormente. Execute o seguinte comando para criar uma [*class Library*](https://learn.microsoft.com/en-au/dotnet/standard/class-libraries?ref=azurebrasil.cloud):

```bash
dotnet new classlib -n MySolution.SharedLocalizations
```

Adicione o projeto criado à solução principal:

```bash
dotnet sln add ./MySolution.SharedLocalizations/MySolution.SharedLocalizations.csproj
```

Primeiro, vamos criar a classe `Culture` que representará as culturas que os aplicativos suportarão. Essa classe armazena informações como o `CultureInfo`, o nome de exibição da cultura e se ela é a cultura padrão.

```csharp
using System.Globalization;

namespace MySolution.SharedLocalizations;

public class Culture(
    CultureInfo cultureInfo, 
    string displayName,
    bool isDefault = false)
{
    public CultureInfo CultureInfo { get; set; } = cultureInfo;
    public string DisplayName { get; set; } = displayName;
    public bool IsDefault { get; set; } = isDefault;
}
```

Em seguida, definimos as culturas que serão suportadas inicialmente. No exemplo, vamos suportar **Português (pt)** e **Inglês (en)**.

```csharp
using System.Globalization;

namespace MySolution.SharedLocalizations;

public static class SupportedCultures
{
    public static List<Culture> Cultures =>
    [
        new(new CultureInfo("pt"), "Português", true),
        new(new CultureInfo("en"), "English")
    ];

    public static List<CultureInfo> CultureInfos => Cultures
        .Select(x => x.CultureInfo)
        .ToList();
}
```

Vamos criar uma classe de marcação, chamada `SharedResources`. [Ela será usada como referência](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization/make-content-localizable?view=aspnetcore-8.0&ref=azurebrasil.cloud#shared-resources) pelos aplicativos que utilizarão os recursos de localização. Essa classe é essencial para que o sistema de localização saiba onde encontrar os arquivos `.resx`.

```csharp
namespace MySolution.SharedLocalizations;

public class SharedResources;
```

Agora, podemos criar os arquivos de recursos (`.resx`) que conterão as traduções. Estes arquivos devem ser armazenados em um diretório chamado **Resources**. 

Para o projeto, vamos criar três arquivos:

- `SharedResources.resx` (arquivo *default*)
- `SharedResources.pt.resx` (para português)
- `SharedResources.en.resx` (para inglês)

#### Arquivo `SharedResources.resx`

```xml
<?xml version="1.0" encoding="utf-8"?>
<root>
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:element name="root" msdata:IsDataSet="true"></xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>1.3</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="Books" xml:space="preserve">
        <value>Livros</value>
    </data>
</root>
```

#### Arquivo `SharedResources.pt.resx`

```xml
<?xml version="1.0" encoding="utf-8"?>
<root>
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:element name="root" msdata:IsDataSet="true"></xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>1.3</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="Books" xml:space="preserve">
        <value>Livros</value>
    </data>
</root>
```

#### Arquivo `SharedResources.en.resx`

```xml
<?xml version="1.0" encoding="utf-8"?>
<root>
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:element name="root" msdata:IsDataSet="true"></xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>1.3</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="Books" xml:space="preserve">
        <value>Books</value>
    </data>
</root>
```

No projeto atual, observamos que todos os arquivos contêm apenas uma localização definida, que tem a chave ***Books***. 

Temos uma visão geral do projeto ***MySolution.SharedLocalizations:***

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-25-at-08.51.24.png)

Vamos criar um novo projeto MVC com o seguinte comando:

```bash
dotnet new mvc -n MySolution.WebApp
```

Agora, adicione o projeto criado à solução principal:

```bash
dotnet sln add ./MySolution.WebApp/MySolution.WebApp.csproj
```

Precisamos configurar o suporte a localizações no arquivo `Program.cs`. Vamos adicionar as linguagens suportadas e as opções de localização, conforme mostrado abaixo:

```csharp
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
    options.DefaultRequestCulture = new RequestCulture(SupportedCultures.Cultures.First(x => x.IsDefault).CultureInfo);
    options.SupportedCultures = SupportedCultures.CultureInfos;
    options.SupportedUICultures = SupportedCultures.CultureInfos;
});
```

Também devemos configurar o caminho onde os arquivos de recurso de localização estarão:

```csharp
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
```

Agora, precisamos garantir que o ASP.NET Core utilize as localizações configuradas:

```csharp
app.UseRequestLocalization();
```

A configuração está feita! Vamos modificar a [*view*](https://learn.microsoft.com/aspnet/core/mvc/views/overview?view=aspnetcore-8.0&ref=azurebrasil.cloud) **Index** da *Home* para exibir a localização ***Books***. Aqui está o conteúdo atualizado da view:

```csharp
@using Microsoft.Extensions.Localization
@using MySolution.SharedLocalizations
@inject IStringLocalizer<SharedResources> Localizer

@{
    ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4">@Localizer["Books"]</h1>
</div>
```

O [*IStringLocalizer<SharedResources>*](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization/make-content-localizable?view=aspnetcore-8.0&ref=azurebrasil.cloud#istringlocalizer) está sendo injetado na *view*, permitindo que o texto localizado de ***Books*** seja exibido na linguagem requisitada pelo usuário.

Agora podemos rodar a aplicação e acessá-la no navegador utilizando o seguinte comando:

```bash
dotnet run
```

```bash
https://localhost:7131/
```

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-16.28.21.png)

Observe que, como não definimos a linguagem ainda (isso será feito por meio de *query string*), ele exibe a localização na linguagem **default**.

Agora, vamos modificar a URL, adicionando `?culture=EN` à query string. Dessa forma, estamos pedindo ao aplicativo para exibir a localização em inglês:

```bash
https://localhost:7131/?culture=EN
```

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-16.29.00.png)

Tudo ocorreu perfeitamente! Vamos agora introduzir um problema que encontramos em muitos projetos que utilizam localizações: **há muitas localizações** no site e para diversas linguagens. 

Os desenvolvedores gastam muito tempo traduzindo e adicionando as localizações manualmente nos arquivos `.resx`. Para resolver esse problema, vamos criar um [**Console app**](https://learn.microsoft.com/en-au/visualstudio/get-started/csharp/tutorial-console?view=vs-2022&ref=azurebrasil.cloud) que automatize esse processo de tradução utilizando o [**Azure AI Translator**.](https://azure.microsoft.com/products/ai-services/ai-translator/?ref=azurebrasil.cloud)

Primeiramente, vamos criar um recurso de nuvem. Veja a documentação oficial:  
[Azure AI Translator Documentation](https://learn.microsoft.com/en-us/azure/ai-services/translator/translator-overview?ref=azurebrasil.cloud)

Primeiramente, vamos criar o recurso de nuvem para utilizar o **Azure AI Translator**

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-15.36.33-4-1.png)

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-15.38.07-3-1.png)

Agora, vamos criar um projeto console que automatiza a manipulação das *localizations*:

```bash
dotnet new console -n MySolution.LocalizationGenerator
```

Adicione o projeto criado à solução principal:

```bash
dotnet sln add ./MySolution.LocalizationGenerator/MySolution.LocalizationGenerator.csproj
```

Agora, podemos adicionar os pacotes necessários ao projeto:

```bash
dotnet add package Spectre.Console
dotnet add package Spectre.Console.Cli
dotnet add package Azure.AI.Translation.Text
```

Crie a classe `TranslatorService` que utilizará o [SDK do **Azure AI Translator**](https://learn.microsoft.com/en-us/azure/ai-services/translator/text-sdk-overview?tabs=csharp&ref=azurebrasil.cloud) para realizar as traduções automáticas:

```csharp
using Azure;
using Azure.AI.Translation.Text;

namespace MySolution.LocalizationGenerator;

public class TranslatorService
{
    private const string ApiKey = "<your-api-key>";
    private const string Endpoint = "<your-endpoint>";
    private const string Region = "<your-region>";
    
    
    public string Translate(string value, string sourceLanguage, string targetLanguage)
    {
        var client = new TextTranslationClient(new AzureKeyCredential(ApiKey), new Uri(Endpoint), Region);
        
        if (sourceLanguage == targetLanguage)
        {
            return value;
        }
        
        var result = client.Translate(targetLanguage, value, sourceLanguage);
        return result.Value.First().Translations.First(x => x.TargetLanguage == targetLanguage).Text;
    }
}
```

Substitua `<your-api-key>`, `<your-endpoint>`, e `<your-region>` pelas [credenciais](https://learn.microsoft.com/en-us/azure/ai-services/translator/text-sdk-overview?tabs=csharp&ref=azurebrasil.cloud#3-authenticate-the-client) obtidas no Azure Portal.

Agora, crie o `ResourceFileService` para manipular os arquivos `.resx`, adicionando e gerando localizações:

```csharp
using System.Xml.Linq;

namespace MySolution.LocalizationGenerator;

public class ResourceFileService
{
    public string ResourcePath
    {
        get
        {
            string currentDirectory = Environment.CurrentDirectory;
            
            DirectoryInfo parentDirectory = Directory.GetParent(currentDirectory)!;
            
            string newPath = Path.Combine(parentDirectory.FullName, "MySolution.SharedLocalizations","Resources");
            
            return newPath;
        }
    }

    public string?[] ListResourceFiles()
    {
        string[] files = Directory.GetFiles(ResourcePath);
        
        return files
            .Select(Path.GetFileName)
            .Where(x => !string.IsNullOrWhiteSpace(x))
            .ToArray();
    }
    
    public string?[] ListResourceFilesIsoLanguages()
    {
        return ListResourceFiles()
            .Select(ExtractLanguageCode)
            .ToArray();
    }
    
    public string ExtractLanguageCode(string? filename)
    {
        if (string.IsNullOrWhiteSpace(filename))
        {
            return string.Empty;
        }
        
        string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filename);
        
        string[] parts = fileNameWithoutExtension.Split('.');
        if (parts.Length == 2)
        {
            return parts[1];
        }

        return string.Empty;
    }
    
    public void AddDataLocalization(string filePath, string key, string value)
    {
        XDocument doc = XDocument.Load(filePath);
        
        var resx = doc.Element("root");
        
        resx!.Add(new XElement("data",
            new XAttribute("name", key),
            new XAttribute(XNamespace.Xml + "space", "preserve"),
            new XElement("value", value)
        ));
        
        resx.Save(filePath);
    }
    
    public void AddDataLocalizations(string filePath, IList<(string Key, string Value)> localizations)
    {
        XDocument doc = XDocument.Load(filePath);
        
        var resx = doc.Element("root");

        foreach (var localization in localizations)
        {
            resx!.Add(new XElement("data",
                new XAttribute("name", localization.Key),
                new XAttribute(XNamespace.Xml + "space", "preserve"),
                new XElement("value", localization.Value)
            ));
        }
        
        resx!.Save(filePath);
    }
    
    public void CreateFile(string filePath)
    {
        XDocument doc = new XDocument(
            new XDeclaration("1.0", "utf-8", null),
            new XElement("root",
                new XElement(XNamespace.Get("http://www.w3.org/2001/XMLSchema") + "schema",
                    new XAttribute("id", "root"),
                    new XAttribute(XNamespace.Xmlns + "xsd", "http://www.w3.org/2001/XMLSchema"),
                    new XAttribute(XNamespace.Xmlns + "msdata", "urn:schemas-microsoft-com:xml-msdata"),
                    new XElement(XNamespace.Get("http://www.w3.org/2001/XMLSchema") + "element",
                        new XAttribute("name", "root"),
                        new XAttribute(XNamespace.Get("urn:schemas-microsoft-com:xml-msdata") + "IsDataSet", "true")
                    )
                ),
                new XElement("resheader",
                    new XAttribute("name", "resmimetype"),
                    new XElement("value", "text/microsoft-resx")
                ),
                new XElement("resheader",
                    new XAttribute("name", "version"),
                    new XElement("value", "1.3")
                ),
                new XElement("resheader",
                    new XAttribute("name", "reader"),
                    new XElement("value", "System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
                ),
                new XElement("resheader",
                    new XAttribute("name", "writer"),
                    new XElement("value", "System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
                )
            )
        );
         
        doc.Save(filePath);
    }
    
    public IList<(string Key, string Value)> ListLocalization(string filePath)
    {
        XDocument doc = XDocument.Load(filePath);
        
        var dataElements = doc.Descendants("data");
        
        var dataKeys = dataElements
            .Select(data => (
                Key: data.Attribute("name")?.Value ?? string.Empty,
                Value: data.Element("value")?.Value ?? string.Empty
            ))
            .ToList();

        return dataKeys;
    }
}
```

Já podemos fazer modificações no `Program.cs`

```csharp
using MySolution.LocalizationGenerator;
using MySolution.SharedLocalizations;
using Spectre.Console;

const string availableLanguages = "\U0001F440 See available languages";
const string createdResourceFiles = "\U0001F440 See existings resource files";
const string addLocalization = "\U0001F449 Add localization";
const string addNewResourceFile = "\U0001F4D6 Add new resource file";

string[] options = [
    availableLanguages,
    createdResourceFiles,
    addLocalization,
    addNewResourceFile
];

while (true)
{
    AnsiConsole.Clear();

    var panel = new Panel("Localizations generator")
    {
        Header = new PanelHeader("My Solution"),
        Padding = new Padding(3, 0, 3, 0),
        Border = BoxBorder.Rounded
    };

    AnsiConsole.Write(panel);
    
    var option = AnsiConsole.Prompt(
        new SelectionPrompt<string>()
            .PageSize(10)
            .Title("Select an action:")
            .AddChoices(options));

    switch (option)
    {
        case availableLanguages:
            HandleAvailableLanguages();
            break;
    
        case createdResourceFiles:
            HandleCreatedResourceFiles();
            break;
        
        case addLocalization:
            HandleAddLocalization();
            break;
    
        case addNewResourceFile:
            HandleAddNewResourceFile();
            break;
    }
}

void HandleAddLocalization()
{
    AnsiConsole.WriteLine($"Add localization:");

    var key = AnsiConsole.Prompt(
        new TextPrompt<string>("\U0001F449 Type the localization [bold yellow]KEY[/] ?"));
    
    var value = AnsiConsole.Prompt(
        new TextPrompt<string>("\U0001F449 Type the localization [bold green]DEFAULT VALUE[/] ?"));
    
    var resourceFileService = new ResourceFileService();
    var translatorService = new TranslatorService();
    
    var files = resourceFileService.ListResourceFiles();

    AnsiConsole.WriteLine();
    
    foreach (var file in files)
    {
        var path = Path.Combine(resourceFileService.ResourcePath, file!);
        var languageCode = resourceFileService.ExtractLanguageCode(file);

        var targetLanguageValue = string.IsNullOrWhiteSpace(languageCode) ? 
            value : 
            translatorService.Translate(value, "pt", languageCode);
        
        resourceFileService.AddDataLocalization(path, key, targetLanguageValue);
        
        AnsiConsole.MarkupLine(string.Format(
            "\U0001F4AC The localization with key [bold yellow]{0}[/] and value [bold green]{1}[/] was added to [bold white]{2}[/]", 
            key, 
            targetLanguageValue, 
            file));
    }
    
    AnsiConsole.WriteLine();
        
    Continue();
}

void HandleCreatedResourceFiles()
{
    var service = new ResourceFileService();
    var files = service.ListResourceFiles();
    
    AnsiConsole.WriteLine("Existing resources:");
    
    var table = new Table()
    {
        Border = TableBorder.Rounded,
        ShowRowSeparators = true
    };
    
    table.AddColumn(new TableColumn("File name").Centered());
    table.AddColumn(new TableColumn("ISO language name").Centered());

    foreach (var file in files)
    {
        var isoCode = service.ExtractLanguageCode(file);
        table.AddRow(file!, string.IsNullOrWhiteSpace(isoCode) ? "--//--" : isoCode);
    }
    
    AnsiConsole.Write(table);
    
    Continue();
}

void HandleAvailableLanguages()
{
    AnsiConsole.WriteLine("Available languages:");
    
    var table = new Table()
    {
        Border = TableBorder.Rounded,
        ShowRowSeparators = true
    };
    
    table.AddColumn(new TableColumn("Name").Centered());
    table.AddColumn(new TableColumn("ISO name").Centered());
    table.AddColumn(new TableColumn("Is default?").Centered());

    foreach (var culture in SupportedCultures.Cultures)
    {
        table.AddRow(
            culture.DisplayName, 
            culture.CultureInfo.Name, 
            culture.IsDefault.ToString());
        
    }
    
    AnsiConsole.Write(table);

    Continue();
}

void HandleAddNewResourceFile()
{
    var isoName = AnsiConsole.Prompt(
        new TextPrompt<string>("\U0001F449 Type the language [bold white]ISO name[/] ?"));
    
    var resourceFileService = new ResourceFileService();
    var translatorService = new TranslatorService();
    
    var file = $"SharedResources.{isoName}.resx";
    var defaultFile = $"SharedResources.resx";
    var path = Path.Combine(resourceFileService.ResourcePath, file);
    var defaultPath = Path.Combine(resourceFileService.ResourcePath, defaultFile);
    
    resourceFileService.CreateFile(path);

    var localizations = resourceFileService.ListLocalization(defaultPath);

    var translatedLocalizations = localizations
        .Select(x => (x.Key, translatorService.Translate(x.Value, "pt", isoName)))
        .ToList();
    
    resourceFileService.AddDataLocalizations(path, translatedLocalizations);
    
    AnsiConsole.WriteLine();
    
    AnsiConsole.MarkupLine($"\U0001F4C4 The localization file [bold white]{file}[/] was created");
    
    Continue();
}

void Continue()
{
    while (true)
    {
        AnsiConsole.WriteLine();
        var confirmation = AnsiConsole.Prompt(
            new TextPrompt<bool>("[bold green]Continue[/] ?")
                .AddChoice(true)
                .AddChoice(false)
                .DefaultValue(true)
                .WithConverter(choice => choice ? "y" : "n"));
        
        if (confirmation)
        {
            break;
        }
    }
}
```

Uma breve explicação:

- **O que faz**: esse código é um gerador de traduções para vários idiomas, onde você pode adicionar e gerenciar traduções nos arquivos de recursos (.resx) da aplicação.
- **Bibliotecas que aparecem**:`Spectre.Console`: deixa o console mais estiloso e interativo.
- **Menu principal**:Mostra 4 opções:Ver os idiomas que a aplicação suporta.Ver os arquivos de recursos já criados.Adicionar uma nova tradução.Criar um novo arquivo de recursos para um idioma novo.
- **Funções principais**:**HandleAddLocalization**: pergunta uma chave e o valor da tradução, traduz o valor para outros idiomas e adiciona nos arquivos certos.**HandleCreatedResourceFiles**: lista os arquivos de recursos que já existem e mostra eles em uma tabela.**HandleAvailableLanguages**: mostra todos os idiomas que a aplicação suporta, com nome e código ISO.**HandleAddNewResourceFile**: cria um novo arquivo de recursos para um idioma com base no código ISO e já preenche com traduções automáticas.
- **Serviços usados**:`ResourceFileService`: faz o trabalho pesado de listar, criar e adicionar traduções nos arquivos.`TranslatorService`: traduz os textos para o idioma certo automaticamente.
- **Visual no console**:Usa painéis e tabelas com bordas estilosas e mensagens formatadas para ficar bonito no terminal.
- **Função para seguir em frente**:**Continue()**: Fica esperando você confirmar se quer continuar ou não.

  
Para iniciar o processo, vamos rodar o **Console app** com o comando:

```bash
dotnet run
```

Agora, dentro do menu do Console app, selecionamos a opção para **verificar as linguagens disponíveis**. Em seguida, verificamos os **arquivos `.resx`** que já existem no projeto, onde vemos as localizações atuais configuradas.

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-16.30.38.png)

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-16.30.43.png)

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-16.30.48.png)

Podemos adicionar uma nova localização com a **Key: `Library`** e **Value: `biblioteca`**. 

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-16.32.26.png)

O *Console app* automaticamente irá adicionar essa chave nos arquivos `.resx` para todas as linguagens disponíveis, e as traduções serão geradas com base na linguagem de cada arquivo.

Arquivo `.resx` em Português:

```xml
  <data name="Library" xml:space="preserve">
    <value>Biblioteca</value>
  </data>
```

Arquivo `.resx` em Inglês:

```xml
  <data name="Library" xml:space="preserve">
    <value>Library</value>
  </data>
```

No **aplicativo MVC**, vamos adicionar a nova localização na página **Index.cshtml**:

```html
<h1 class="display-4">@Localizer["Library"]</h1>
```

Agora, vamos rodar a aplicação com o comando:

```bash
https://localhost:7131/
```

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-16.34.49.png)

Definindo a cultura para inglês (`culture=EN`):

```bash
https://localhost:7131/?culture=EN
```

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-16.34.56.png)

Verificamos que tudo ocorreu bem e as localizações estão funcionando conforme esperado.

Agora, vamos adicionar o suporte ao idioma **espanhol** na classe `SupportedCultures` do projeto **MySolution.SharedLocalizations**:

```csharp
new(new CultureInfo("es"), "español")
```

No **Console App**, podemos ver que agora temos **espanhol** como uma nova linguagem suportada, porém, ainda não temos o arquivo **SharedResources.es.resx**.

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-16.40.51.png)

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-16.40.56.png)

No menu principal do **Console App**, selecionamos a opção **"Add new resource file"** e escolhemos a linguagem **"es"** (espanhol). O arquivo **SharedResources.es.resx** será criado automaticamente, e as traduções já serão aplicadas a partir do **Azure AI Translator**.

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-16.41.29.png)

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-16.41.39.png)

```xml
 <data name="Books" xml:space="preserve">
    <value>Libros</value>
  </data>
  <data name="Library" xml:space="preserve">
    <value>Biblioteca</value>
  </data>
```

Ao selecionar a opção de listar os arquivos `.resx`, podemos verificar que o arquivo **SharedResources.es.resx** foi criado corretamente e as localizações já estão traduzidas.

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-16.43.59.png)

Agora podemos testar a nova linguagem no **aplicativo MVC** utilizando a query string para o idioma espanhol.

```bash
https://localhost:7131/?culture=ES
```

![](https://storage.ghost.io/c/00/52/0052dced-0017-4d07-b190-1f5c48e0ab59/content/images/2024/09/Screen-Shot-2024-09-24-at-16.43.22.png)

E é isso, pessoal! Esta foi uma breve apresentação sobre como configurar e automatizar localizações em um projeto .NET utilizando o **Azure AI Translator**. Este projeto pode ser extremamente útil e economizar muito tempo no desenvolvimento, ao gerenciar localizações automaticamente com base nas linguagens suportadas.

Você já pode baixar o projeto por esse [link](https://github.com/TallesValiatti/CreateLocalizationsAutomatically?ref=azurebrasil.cloud), e não esquece de me seguir no [LinkedIn](https://www.linkedin.com/in/tallesvaliatti/?ref=azurebrasil.cloud)!

Até a próxima, abraços!